版权声明:本文为博主原创文章,未经博主同意不得转载。

https://blog.csdn.net/u012974916/article/details/24580405

一  Handler作用和概念 

    包括线程队列和消息队列。实现异步的消息处理机制。跟web开发的ajax有异曲同工之妙。

 

      1.运行在某个线程上,共享线程的消息队列。 
      2.接收消息、调度消息,派发消息和处理消息; 
      3.实现消息的异步处理;

        Handler能够让你发送和处理消息,以及Runnable对象。每一个Handler对象相应一个Thread和Thread的消息队列。

当你创建一个Handler时,它就和Thread的消息队列绑定在一起,然后就能够传递消息和runnable对象到消息队列中,运行消息后就从消息队列中退出。

  Handler的作用就是:调度消息和runnable对象去被运行;使动作在不同的线程中被运行。

  当一个应用程序中进程被创建时。它的主线程专门运行消息队列(messageQueue),去管理顶层的应用程序相关的对象如:activity,broadcastReceiver。windows等。你能够创建你的Thread,和主线程进行交互——通过Handler,交互的方法就是通过post或者sendMessage。可是在你的新线程中。给定的Message或者Runnable,会在适当的时候的被调度和处理。

(即不会被马上处理——堵塞式)。

实际上就是建立消息处理模型/系统

   要学习Handler,看到肯定是和消息有关,可能还是须要先熟悉一下消息系统的构成和简单原理。

以下就先学习一下消息系统的基本原理。

二 消息系统的基本原理和构成

       从一般的消息系统模型的建立大致构成以下几个部分:

    1. 消息原型

    2. 消息队列

    3. 发送消息

    4.消息循环

    5.消息获取

    6.消息派发

    7.消息处理

大致模型图例如以下:

 

    

 消息系统模型通常会包括以上七个部分(消息原型,消息队列,消息发送,消息循环。消息获取,

消息派发。消息处理)。实际上的核心是消息队列和消息循环,其余部分都是环绕这两部分进行的。

  从前面文档的分析中我们知道Handler就是用来建立消息处理的系统模型,那么和这里基本消息

系统模型相比。那么Handler又是怎样囊括这七个部分的呢?

  在Android中对这六个部分进行了抽象成四个独立的部分(消息循环):

    Handler,Message,MessageQueue,Looper。

  •   Message就是消息原型。包括消息描写叙述和数据。
  •   MessageQueue就是消息队列。
  •   Looper完毕消息循环
  •   Handler就是驾驭整个消息系统模型。统领Message,MessgeQueue和Looper;

  Handler能够实现消息系统模型,那么详细是怎样进行工作的呢,以下探究一下这当中工作的方法和原理。

三 Handler工作原理分析

  要了解Handler工作原理,先看一下这个系统模型详细组成的层次结构框架是个什么样的。

      

1.Looper:

  实现Thread的消息循环和消息派发,缺省情况下Thread是没有这个消息循环的既没有Looper;

须要主动去创建。然后启动Looper的消息循环loop;与外部的交互通过Handler进行;

2.MessageQueue:

  消息队列,由Looper所持有,可是消息的增加是通过Handler进行;

  消息循环和消息队列都是属于Thread。而Handler本身并不具有Looper和MessageQueue;

可是消息系统的建立和交互。是Thread将Looper和MessageQueue交给某个Handler维护建立消息系统模型。所以消息系统模型的核心就是Looper。

消息循环和消息队列都是由Looper建立的。

而建立Handler的关键就是这个Looper。

  一个Thread同一时候能够相应多个Handler,一个Handler同一时候仅仅能属于一个Thread。Handler属于哪个

Thread取决于Handler在那个Thread中建立。

  在一个Thread中Looper也是唯一的。一个Thread相应一个Looper,建立Handler的Looper来自哪个Thread。

Handler属于哪个Thread。

  故建立Thread消息系统,就是将Thread的Looper交给Handler去打理,实现消息系统模型,完毕消息的异步处理。

Handler与Thread及Looper的关系能够用以下图来表示:

    

 

       Handler并不等于Thread,必须通过Thread的Looper及其MessageQueue,用来实现Thread消息系统模型,依附于Thread上。

在线程建立Handler时:

  使Handler满足消息系统须要的条件,将Thread中的Looper和MessageQueue交给Handler来负责维护。

在线程中建立Handler,须要做以下工作:

  1.  获取Thread中的Looper交给Handler的成员变量引用维护;

  2.  通过Looper获取MessageQueue交给Handler的成员变量引用维护。

  那么消息系统模型建立完毕之后,依照消息系统运行。从Handler来看就是发送消息派发消息,与此线程消息系统的交互都由Handler完毕。

消息发送和派发接口:

  1.  post(runnable)消息,Runnable是消息回调,经过消息循环引发消息回调函数运行。

  2.  sendMessage(Message)消息。经过消息循环派发消息处理函数中处理消息;

  3.  dispatchMessage       派发消息,若是post或带有回调函数则运行回调函数,否则运行

      消息处理函数Handler的handleMessage(通常派生类重写)。

 以上就是Handler怎样实现Thread消息系统模型的大致介绍。

以下将详细分析是怎样实现消息系统模型运行的。

四 Handler实现流程分析

  我们知道Handler就是一个消息系统的外壳。属于某个Thread并包装了Thread的Looper及其MessageQueue。与外部进行交互(同一个线程内或者线程之间),接收派发和处理消息,消息系统模型的核心是Looper。

  以下看看Handler是怎样建立跑起来的。以msg消息为例,runnable实质是一样。

1 Handler的建立

  Handler唯一属于某个Thread,在某个Thread中建立Handler时。须要获取Thread的Looper

及其MessageQueue,建立Handler关键是Looper的来源。

  Handler提供了好几个构造函数但其本质一致:由外部传入Looper:当前线程或其它线程 
  

  public Handler(Looper looper) {
        //初始化构建消息系统參数
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
  }

从当前线程获取:由创建Handler的Thread决定

       

  public Handler() {
        //初始化构建消息系统參数
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = null;
  }   public static Looper myLooper() {
return sThreadLocal.get();
}

 无论哪种方式,我们知道Thread在默认情况下是没有建立消息循环Looper实例的。要实现消息循环必须确保Thread的Looper建立。怎样确保呢?

Looper提供了静态函数:

public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
} sThreadLocal.set(new Looper());
} //存储线程的局部变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

看到这里刚開始让我非常是奇怪和迷惑:

     Looper一个独立的类。又不属于某个Thread,而这里创建Looper的函数又是静态的,

属于整个Looper类;创建Looper之后交给静态成员变量sThreadLocal保存,获取

sThreadLocal.get()。那么一个静态变量属于整个类,属性更改始终有效。一次创建之后

sThreadLocal.get()永远都不等于null!

  而Thread和Looper是唯一相应的。那这里岂不是全部的Thread都是用同一个Looper,不可能!所以肯定这个ThreadLocal是有玄机的。网上一查:

  ThreadLocal:

    维护线程的变量,为每一个使用该变量的线程实例提供独立的变量副本。每一个线程都能够独立使用该变量,而互不影响。

(详细可參考:http://blog.csdn.net/qjyong/article/details/2158097

  所以每一个线程调用Looper.prepare时,都会创建为其唯一的Looper。要建立Handler,须要先创建线程的Looper,才干建立消息系统模型。

通过Looper我们建立了Thread上的消息系统模型Handler,能够来进行消息系统的一系列流程了。

2 消息发送

消息发送两种方式:post和sendMessage。

       post:针对runnable对象;Runnable是一个接口,就是一个回调函数(提供了run方法)

       sendMessage:针对Message对象;

       以下通过代码详细看一下这个过程:

public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
} public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}

 

  看到post和sendMessage发送消息时,仅仅是对象不同而已,Runnable和Message。但实际上都是Message的形式来描写叙述。

这跟我通常理解的消息机制不同:

  通常post消息是将消息增加到消息队列中并不马上运行就返回,send消息是马上运行等待消息运行完才返回。而这里post或者send都是将消息放入到消息队列中。然后马上返回,等待消息循环时获取消息被运行。

  这里提供了众多的消息发送方法来指定消息的运行时间和顺序。详细能够查看源代码。

消息运行顺序是依据消息队列中消息的排列顺序而定。以下看一下发送消息后将消息增加到消息队列中的代码:

由Handler调用MessageQueue的enqueueMessage方法:

       

  final boolean enqueueMessage(Message msg, long when) {

              Message p = mMessages;

              if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
}
else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
} msg.next = prev.next;
prev.next = msg;
}
……
  }

  能够看到是依照时间顺序将消息增加到MessageQueue中;

如今将消息增加到消息队列中存储起来。消息并未得到处理。下一步必定是怎样派发消息和处理消息。

3 消息派发

建立Thread消息循环由Looper完毕,存在一个消息调度死循环:    

  public static void loop() {
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
} //派发消息 到target(Handler)
            msg.target.dispatchMessage(msg);             //回收Msg到msgPool
msg.recycle();
}
}
  }

  这里看到消息派发是由Message的target完毕,这个target是什么呢?是一个Handler。消息系统是通过Handler用来与外部交互,把消息派发出去。能够看到没有这个Handler,消息循环将结束。

消息派发由Looper通过Handler完毕:

  public void dispatchMessage(Message msg) {

       //首先推断runnable对象
if (msg.callback != null) {
handleCallback(msg);
}
else {
//整个消息系统的回调函数 能够不用实现自己Handler
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//消息处理 通常交给Handler派生类
handleMessage(msg);
}
  }

  通过消息派发,这样就实现消息的异步处理。

4 消息原型

前面看到消息发送有两种方式:

  post(Runnable对象),sendMessage(Message对象)。而中间都是通过Message对象

保存在MessageQueue中。然后消息派发时处理方式不同。假设在sendMessage时将将消息对象附上Runnable对象,则post和sendMessage没有差别了。

所以这两种方式非常好理解基本一致。处理的方式不同罢了。

  消息系统模型中,我们的真正的消息原型是什么。都具有那些功能,以下看一下Message中究竟包括了那些东西,能有效帮助我们合理的运用消息系统来完毕一些任务和处理。

Message消息原型:

  public final class Message implements Parcelable {
  //标识消息
  public int what;
  int flags;
  long when;   //传递简单数据
   public int arg1;
  public int arg2;   //传递较复杂数据 对象
  public Object obj;
  Bundle data;   //处理消息的目标Handler
  Handler target;   //消息派发时 运行的Runnable对象
  Runnable callback;   //使消息形成链表
  Message next;   //建立一个消息pool,回收msg,以避免反复创建节约开销
  private static Message sPool;
  private static int sPoolSize = 0;
  private static final int MAX_POOL_SIZE = 10;
  }  

  看到提供了非常丰富的属性来描写叙述消息,针对详细问题选择使用那些属性去怎么样描写叙述消息了。

获取新的Message对象时,Message提供了obtain方法:避免我们自己去分配Message新的对象,通过obtain获取,可能从MessagePool中获取。节约开销。

以下看一下这个MessagePool是怎样建立的:

  通常消息处理完毕的时候。消息也基本上处于无用状态能够释放回收了。对于须要频繁的创建释放的对象来说,创建和释放类实例都是要开销的,太频繁的使开销增大不好,像Message这样的非常有可能会频繁的创建。

于是我们能够将创建的对象用完之后保存在一个Pool里面,以便再反复利用节约频繁创建释放开销。

是怎样建立的呢?必定是在消息处理完毕之后才干进行。

MessagePool建立:

public static void loop() {
while (true) {
//派发消息
msg.target.dispatchMessage(msg); //消息处理完毕 回收
        msg.recycle();
    }
} public void recycle() {
//回收Message 建立全局的MessagePool
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}

五 Handler的应用

  以上这就是整个Handler作用及消息系统模型的建立。

使用也非常easy,尽管有非常多方式,但仅仅要理解Handler是建立在Looper上,实现Thread的消息系统处理模型,实现消息异步处理,我想对与Handler基本应用上没有什么不能理解的了。其它方面能够去看源代码了。

  Handler使用起来是非常easy的,关键就是怎样利用消息的异步处理。来合理的完毕我们须要功能和任务。

对于一个Thread,我们使用好几个Handler来进行异步处理。也能够创建新的Thread,通过Handler来实现消息异步处理等等,应用场景非常多怎样用的好用的合理,这就没什么经验了。

Handler有何作用?怎样使用?的更多相关文章

  1. Handler.removeMessages的作用,有时候为什么一定要先remove一下呢

    removeMessages会将handler对应message queue里的消息清空,如果带了int参数则是对应的消息清空.队列里面没有消息则handler会不工作,但不表示handler会停止. ...

  2. 阶段一:用Handler和Message实现计时效果及其中一些疑问

    “阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 本来是打算继续做天气预报的优化的,但因为某些原因,我要先把之前做的小应用优化一下.所以今天就插播一下用Handle ...

  3. 讲讲Handler+Looper+MessageQueue 关系

    Handler+Looper+MessageQueue这三者的关系其实就是Android的消息机制.这块内容相比开发人员都不陌生,在面试中,或者日常开发中都会碰到,今天就来讲这三者的关系. 概述: H ...

  4. 深入理解Message, MessageQueue, Handler和Looper

    做过Android的都知道Message, MessageQueue, Handler和Looper,但知道不代表你理解它们.有时觉得用得很顺手,但Android怎么实现又说不上来,总觉得似懂非懂.不 ...

  5. 学习通过Thread+Handler实现非UI线程更新UI组件

    [Android线程机制] 出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则 ...

  6. Android子线程更新UI主线程方法之Handler

    背景: 我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的. ...

  7. android handler工作原理

    android handler工作原理 作用 便于在子线程中更新主UI线程中的控件 这里涉及到了UI主线程和子线程 UI主线程 它很特别.通常我们会认为UI主线程将页面绘制完成,就结束了.但是它没有. ...

  8. Android菜鸟的成长笔记(12)——Handler、Loop、MessageQueue

    原文:[置顶] Android菜鸟的成长笔记(12)——Handler.Loop.MessageQueue 当一个程序第一次启动时,Android会启动一条主线程(Main Thread),主线程主要 ...

  9. Handler和HandlerThread

    1.什么是Handler? SDK中关于Handler的说明例如以下: A Handler allows you to sendand process Messageand Runnable obje ...

随机推荐

  1. 使用反射获取类中的属性(可用于动态返回PO类的列,当做表格的表头)

    //利用反射取类中的属性字段 try { Class clazz = Class.forName("houji.bean.model.TaskModel"); Field[] fi ...

  2. hdu 4951

    Multiplication table Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Oth ...

  3. Requests模拟登陆

    requests模拟登陆知乎网站 实例 # -*- coding: utf-8 -*- __author__ = 'CQ' import requests try: import cookielib ...

  4. WEB学习-CSS基础选择器

    基础选择器 标签选择器 就是标签的名字. • <h1>前端与移动开发<span>1期班</span>基础班</h1> css: • <style ...

  5. Codeforces 552E Vanya and Brackets(枚举 + 表达式计算)

    题目链接 Vanya and Brackets 题目大意是给出一个只由1-9的数.乘号和加号组成的表达式,若要在这个表达式中加上一对括号,求加上括号的表达式的最大值. 我们发现,左括号的位置肯定是最左 ...

  6. WdatePicker.js的使用方法 帮助文档 使用说明(时间控件)*转载

    4. 日期范围限制 静态限制 注意:日期格式必须与 realDateFmt 和 realTimeFmt 一致 你可以给通过配置minDate(最小日期),maxDate(最大日期)为静态日期值,来限定 ...

  7. Vim出现:_arguments:450: _vim_files: function definition file not found的问题解决

    安装了zsh之后使用vim出现如下错误: arguments:450: _vim_files: function definition file not found _arguments:450: _ ...

  8. 解密优秀博士成长史 ——微软亚洲研究院首届博士生学术论坛Panel讨论经验总结

    编者按:有人说“一入博门深似海”,读博前应该做好哪些准备?作为一名博士生,应该有怎样的学术或职业规划?导师还是老板?怎样在师生关系上做到双赢?你是导师心目中优秀的博士生吗?相信以上问题在很多同学心中萦 ...

  9. Matlab中配置VLFeat

    在VLFeat官网上是这么介绍VLFeat的:VLFeat开源库实现了很多著名的机器视觉算法,如HOG, SIFT, MSER, k-means, hierarchical k-means, aggl ...

  10. C++中的sort函数

    (一)为什么要用c++标准库里的排序函数 Sort()函数是c++一种排序方法之一,学会了这种方法也打消我学习c++以来使用的冒泡排序和选择排序所带来的执行效率不高的问题!因为它使用的排序方法是类似于 ...