Android 消息传递机制
引言:在日常开发工作中,想必大家或多或少都会遇到数据传输的需要。在 Android 中,Google 也已经根据各种情况为我们准备好了各种工具,比如Intent
,广播消息机制
,IPC消息机制
,Handler消息机制
等等。Android sdk 中还有许多传输数据的技巧,这里就不一一说明。各种消息机制是适用于各种情况下的,比如广播机制是作为 Android 四大件当中的传递消息的媒体,而 IPC 机制是作为进程间通信的一种方式。而我要讲的 Handler 消息机制是属于线程间通信的一种方式。跟大家唠嗑了这么久,接下来我们就来认识 Handler 的运行机制
目录
- 什么是 Handler
- Handler 机制的运行原理
- Handler、MessageQueue 与 Looper
- 在 UI 线程中
- 在普通线程中
- 运行原理
- Handler 发送消息
- Handler 接收消息
- Handler、MessageQueue 与 Looper
- 总结
- 参考资料
什么是 Handler
其实我相信大家接触到这个 Handler 类都是因为遇到需要执行耗时操作这个需求。然而很不幸的是我们不能直接在 UI 线程中直接执行耗时操作,否则就会抛出经典的 ANR 通知框。而如果我们在主线程开启一个新的线程来执行耗时操作同时将耗时操作的结果直接返回给主线程,然而这又有一个问题就是UI线程是不安全的,如果在非 UI 线程中直接操作 UI 线程则会抛出同样经典的"Only the original thread that created......"异常。这时我们可以通过 Handler 将更新 UI 的操作切换到 UI 线程中运行。这是 Handler 机制的一个特殊使用场景。但 Handler 在 Android 系统还可以完成许多其他任务,比如 Activity 组件生命活动周期函数的回调都是根据 Handler 发送的消息内容去决定回调内容。
Handler 的运行机制需要底层的 MessageQueue 和 Looper 的支撑。而 Handler 的主要作用是将一个任务切换到某个指定的线程中去执行。

Handler 机制的运行原理
Handler、MessageQueue 与 Looper
上面我们谈到了 Handler 的消息机制是需要 MessageQueue 和 Looper 的配合下才能运行。 Google API 文档中对 Handler 有这样一段阐述:每个 Handler 的实例都会与一个线程以及线程的消息队列(也就是 MessageQueue)进行绑定。而什么时候去绑定呢,就是当一个 Handler 实例创建的时候,实例所在的线程就会与新创建出来的 Handler 实例进行绑定。而与 MessageQueue 的绑定其实是通过 Looper 来实现的,因为 MessageQueue 其实是 Looper 类的成员变量。所以关键还得看 Handler 与 Looper 的关联。
在 UI 线程中
Google API 文档中还有一段重要信息:当一个 App 的进程开启时,就会启动一个主线程,即 UI 线程,而在 UI 线程中默认是已经实现了 Looper 的实例化,所以只要在 UI 线程中创建一个 Handler 实例,那么整个 Handler 的消息机制是关联起来的。我们现在从源代码中看看这一过程。
程序开启过程会启动一个 ActivityThread,这也就是我们单线程模型中的那个唯一的主线程,主线程的激活是从它的 main()
方法开始的。
ActivityThread.java
public final class ActivityThread{
......
......
public static void main(String[] args){
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
......
}
}
为了方便大家把关注点放到 Looper 实例的创建,我将无关代码删除,而将相关代码显示出来。我们看见整个main()
方法中关于 Looper 实例的创建就是在Looper.prepareMainLooper()
方法中实现的。我们再往下看源码
Looper.java
public final class Looper{
......
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;
......
public static Looper myLooper(){
return sThreadLocal.get();
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static void prepareMainLooper() {
prepare(false); //关键代码
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
......
}
上面我们可以看见关键代码是prepare(false)
,进入它的源码一看发现,首先会判断当前线程的 ThreadLocal 类有没有存储内容,如果有就会抛出异常,如果没有就会将新建的 Looper 实例放入到当前线程中的 ThreadLocal 进行储存。而我们只需要知道 ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。ThreadLocal 类的特性使得它在某个特定的线程中与特定的 Looper 实例进行一对一的绑定再合适不过了。关于 ThreadLocal 的详细介绍,大家可以查看理解Java中的 ThreadLocal。
接下来我们将关注点放到新建 Looper 实例上来。我们可以看到这个新建过程也是比较简单的。新建过程中直接将 Looper 的成员变量MessageQueue mQueue
实例化,而这个mQueue
就是我们我们整个运行机制需要用到的 MessageQueue 对象。所以到目前为止 UI 线程的 Looper 对象和 MessageQueue 对象就已经准备好了。那刚才上面我们说了新建 Handler 实例的时候就会将 Handler 对象与 Looper 对象进行绑定,至于这一具体过程是怎么样的,我们还需看看 Handler 的源码。
Handler.java
public class Handler{
......
final Looper mLooper;
final MessageQueue mQueue;
......
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
......
}
可以看见,当我们使用无参构造方法来新建一个 Handler 实例,这个缺省构造方法得到的 Handler 实例属于当前线程。我们还发现无参构造方法会调用有参的构造方法,该构造方法中有个重要的语句mLooper = Looper.myLooper()
就会将 UI 线程中通过 ThreadLocal 对象储存的 Looper 对象赋值给 Handler 对象的成员变量mLooper
,这样就已经将 Handler 对象、Looper 对象、MessageQueue 对象全部联系起来,从而各司其职共同承担消息传递机制的运行。
在普通线程中
上面说的是在 UI 线程中,而如果我们自己开启一个普通的线程,同时希望这个线程能够运行消息传递机制,这个时候我们就需要自己完成上面 UI 线程自动完成的部分了。实例代码如下:
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyThread myThread = new MyThread();
myThread.start();
......
}
......
class MyThread extends Thread{
public Handler handler;
@Override
public void run(){
Looper.prepare(); //为当前线程创建一个 Looper
handler = new Handler(){
@Override
public void handleMessage(Message msg){
......
}
};
Looper.loop(); //开启消息循环
}
}
}
UI 线程使用 Handler 对象与在普通线程中使用 Handler 对象不同之处就是在普通线程中我们新建 Looper 对象以及开启 Looper 不断循环的操作在 UI 线程中是已经完成的。而因为Looper.prepare()
的方法在 ActivityThread 类中被Looper.prepareMainLooper
回调有讲到,所以这里就不再重复讲。我们来看看Looper.loop()
方法,而因为源码比较长,这里我就不全部贴出来,只讲讲具体的运行过程。
Looper.loop()
方法是 Looper 类的最重要的一个方法之一。只有调用了该方法,消息循环系统才会真正地起作用。方法内部其实是个死循环,唯一跳出循环的方式是 MessageQueue 的next()
方法返回了 null 值。当 Looper 的quit()
方法被调用时, Looper 就会调用 MessageQueue 的quit()
方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next()
方法就会返回 null 。也就是说, Looper 必须使用quit()
方法来退出循环,否则loop()
方法就会无限循环下去。如果不使用quit()
方法来退出循环,同时消息队列又没有消息,而 MessageQueue 的next()
方法又是阻塞操作。这时next()
方法会一直阻塞在那里,这也导致loop()
方法一直阻塞在那里。
运行原理
首先我们给出整个机制运行的示意图。然后再以此展开阐述。

Handler 发送消息
Handler 类其实提供了许多发送消息的方式,如sendEmptyMessageXXX
、sendMessageXXX
、postXXX
方法。查看源码后都会发现无一例外最后都会调用 Handler 的enqueueMessage()
方法。我们去看看该源码

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这个方法首先将我们要发送的消息 Message 的target
属性设置为当前 Handler 对象(进行关联);接着将msg
与uptimeMillis
这两个参数都传递到 MessageQueue(消息队列)的enqueueMessage()
方法中,所以接下来我们就继续分析 MessageQueue 类的enqueueMessage
方法,如下:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
通过这个方法名可以看出来通过 Handler 发送消息实质就是把消息 Message 添加到 MessageQueue 消息队列中的过程而已。通过上面遍历等next
操作可以看出来,MessageQueue 消息队列是通过类似C语言的链表来存储这些有序的消息的。其中的mMessages
对象表示当前待处理的消息;然后从22到52行可以看出,消息插入队列的实质就是将所有的消息按时间(uptimeMillis 参数,上面有介绍)进行排序。所以还记得上面sendMessageAtFrontOfQueue
方法吗?它的实质就是把消息添加到 MessageQueue 消息队列的头部(uptimeMillis 为0,上面有分析)。
那么问题来了!既然消息都存入到了 MessageQueue 消息队列,当然要取出来消息吧,不然存半天有啥意义呢?我们知道 MessageQueue 的对象在 Looper 构造函数中实例化的;一个 Looper 对应一个 MessageQueue ,所以说 Handler 发送消息是通过 Handler 构造函数里拿到的 Looper 对象的成员 MessageQueue 的enqueueMessage
方法将消息插入队列,也就是说出队列一定也与 Handler 和 Looper 和 MessageQueue 有关系。
Handler 接收消息
我们在分析 Looper 的loop
的方法时发现,Looper 一直在不断的从消息队列中通过 MessageQueue 的next
方法获取 Message ,然后通过代码msg.target.dispatchMessage(msg)
让该msg
所绑定的 Handler(Message.target)执行dispatchMessage
方法以实现对 Message 的处理。
Handler 的 dispatchMessage(Message msg)源码如下
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我们来分析下这段代码。首先会判断msg.callback
存不存在,msg.callback
是Runnable 类型,如果msg.callback
存在,那么说明该 Message 是通过执行 Handler 的 postXXX 系列方法将 Message 放入到消息队列中的,这种情况下会执行handleCallback(msg)
,这样我们我们就清楚地看到我们执行了msg.callback
的run
方法,也就是执行了 postXXX 所传递的 Runnable 对象的run
方法。其源码如下:
private static void handleCallback(Message message){
message.callback.run();
}
如果我们不是通过 postXXX 系列方法将 Message 放入到消息队列中的,那么msg.callback
就是 null ,代码继续往下执行,接着我们会判断 Handler 的成员字段mCallback
存不存在mCallback
是Hanlder.Callback
类型的,我们在上面提到过,在 Handler 的构造函数中我们可以传递Hanlder.Callback
类型的对象,该对象需要实现handleMessage
方法,如果我们在构造函数中传递了该 Callback 对象,那么我们就会让 Callback 的handleMessage
方法来处理 Message 。
如果我们在构造函数中没有传入 Callback 类型的对象,那么mCallback
就为 null ,那么我们会调用 Handler 自身的hanldeMessage
方法,该方法默认是个空方法,我们需要自己是重写实现该方法。
综上,我们可以看到 Handler 提供了三种途径处理 Message ,而且处理有前后优先级之分:首先尝试让 postXXX 中传递的 Runnable 执行,其次尝试让 Handler 构造函数中传入的 Callback的handleMessage
方法处理,最后才是让 Handler 自身的handleMessage
方法处理 Message 。
总结
以现实生活为例。我们可以把 Handler 的运行机制跟生产流水线类比。在现实生活的生产生活中,存在着各种各样的传送带,传送带上面洒满了各种货物,传送带在发动机滚轮的带动下一直在向前滚动,不断有新的货物放置在传送带的一端,货物在传送带的带动下送到另一端进行收集处理。
我们可以把传送带上的货物看做是一个个的 Message ,而承载这些货物的传送带就是装载 Message 的消息队列 MessageQueue 。传送带是靠发送机滚轮带动起来转动的,我们可以把发送机滚轮看做是 Looper ,而发动机的转动是需要电源的,我们可以把电源看做是线程 Thread ,所有的消息循环的一切操作都是基于某个线程的。一切准备就绪,我们只需要按下电源开关发动机就会转动起来,这个开关就是 Looper 的loop
方法,当我们按下开关的时候,我们就相当于执行了 Looper 的loop
方法,此时 Looper 就会驱动着消息队列循环起来。
那 Handler 在传送带模型中相当于什么呢?我们可以将 Handler 看做是放入货物以及取走货物的管道:货物从一端顺着管道划入传送带,货物又从另一端顺着管道划出传送带。我们在传送带的一端放入货物的操作就相当于我们调用了 Handler 的sendMessageXXX
、sendEmptyMessageXXX
或postXXX
方法,这就把 Message 对象放入到了消息队列 MessageQueue 中了。当货物从传送带的另一端顺着管道划出时,我们就相当于调用了 Handler 的dispatchMessage
方法,在该方法中我们完成对 Message 的处理。

参考资料
最后是广告时间,我的博文将同步更新在三大平台上,欢迎大家点击阅读!谢谢