Handler消息传递机制——源码赏析

来源:互联网 发布:河南中小学生消防知 编辑:程序博客网 时间:2024/06/05 12:04

Android的消息处理有四个核心类:Handler 、Looper 、Message 、MessageQueue , 都在android.os包中

一、线程的魔法师 Looper

Looper字面 意思是循环者,它被设计用来使一个普通线程变成一个Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中) ,经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程,使用Looper类创建Looper线程很简单,代码如下:


通过上面两行代码普通线程就编程了Looper线程,看看这两行代码都干了些什么。

1)Looper.prepare();


通过上图可以看出,现在你的线程中有了一个Looper对象,他的内部维护了一个消息队列MQ.  一个Thread只能有一个Looper对象,为什么呢,我们来看下源码


通过源码可以看出,当我们调用Looper.prepare()时,内部会检查该线程中是否存在Looper对象,如果存在就会抛出:Only one Looper may be created per thread

2)Looper.loop();


调用loop方法后,Looper线程就开始真正工作了,它不断的从自己的MQ中取出队头的消息(及任务)执行。源码如下:


从源码中我们可以看出在116行我们得到当前的线程Looper,下面两行代码是得到当前looper的MQ,在123行我们可以清楚的看到里面是一个死循环,就是不断的从MQ中取出消息处理。在124行我们取出消息队列中的消息,137行非常重要,该行是将处理工作交给message的target,即后面将到的handler, 131行是我们记录的日志信息,了解即可。154行将发送出去的消息回收掉。

除了prepare()方法和loop()方法,Looper还提供了一些比较有用的方法,如:

Looper.myLooper() 得到当前线程的Looper对象

getThread() 得到Looper对象所属线程

quit() 结束Looper循环

到此我们该对Looper有了一个大概的了解,总结一下几点:

  每个线程最多只能有一个Looper对象,他是一个ThreadLocal

 Looper内部有一个消息队列,loop()方法调用后,开始不断的从消息队列中取出消息执行

 Looper使一个普通线程变成一个Looper线程


那么我们该如何往MQ中添加消息呢?下面就用到了我们的Handler!

二、异步处理大师 Handler

handler扮演了往MQ中添加消息和处理消息的角色(只处理有自己发送的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程都是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前的looper. 默认的构造方法:


从源码中我们可以看出在第195行将默认关联当前线程的looper, 196行是判断当前looper是否为空,200行代码非常重要,表示直接把关联的Looper的MQ当做自己的MQ,因此它的消息将发送到关联looper的MQ上。

下面我们为之前的代码加上handler


加入handler的效果图如下


可以看到,一个线程可以有多个handler ,但是只能有一个Looper

handler发送消息

有个handler之后我们就可以使用post(Runnable) ,postAtTime(Runnable,long), postDelayed(Runnabel,long), sendEmptyMessage(int) , sendMessage(Message), sendMessageAtTime(Message,long) 和 sendMessageDelayed(Message,long) 这些方法向MQ发送消息了,只看这些方法我们可能觉得handler发送消息有两种方式,一种是Runnable对象,一种是Message对象,其实post发送的Runnable对象最终都被封装成message对象了,见源码:

总之通过handler发出的message有如下特点
1.message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码

msg.target.dispatchMessage(msg); 
2.post发出的message,其callback为Runnable对象
Handler处理消息
说完了消息的发送,再来看下handler如何处理消息。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Messagemsg)完成的,见源码
可以看到,除了handleMessage(Message msg)和Runnable对象的run方法由开发者实现外(实现具体逻辑),handler的内部工作机制对开发者是透明的。这正是handler API设计的精妙之处!
Handler的用处
handler被描述为“异步处理大师”,这归功于Handler拥有下面两个重要的特点:
1.handler可以在任意线程发送消息,这些消息会被添加到关联的MQ上。
2.handler是在它关联的looper线程中处理消息的。
这就解决了android最经典的不能在其他非主线程中更新UI的问题。android的主线程也是一个looper线程(looper在android中运用很广),我们在其中创建的handler默认将关联主线程MQ。因此,利用handler的一个solution就是在activity中创建handler并将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知activity更新UI。(过程如图)
下面给出sample代码,仅供参考:
当然,handler能做的远远不仅如此,由于它能post Runnable对象,它还能与Looper配合实现经典的Pipeline Thread(流水线线程)模式。

三、封装任务 Message

在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,使用Message需要注意4点:

1、Message虽然也可以通过new来获取,但是通常使用Message.obtain()或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源;
2、如果一个Message只需要携带简单的int型数据,应优先使用arg1和arg2属性来传递数据,这样比Bundle节省内存;
3、尽可能使用Message.what来标识信息,以便用不同的方式处理Message。
4、如果需要从工作线程返回很多数据信息,可以借助Bundle对象将这些数据集中到一起,然后存放到obj属性中,再返回到主线程。

0 0