Android Handler消息机制的理解

最近在看《深入理解Android内核设计思想》,看到有关Handler消息机制这部分,以前一直对这块似懂非懂,其实说白了就是还不懂,现在看过这篇后可以说是受益颇多,作者从源码的角度深层次的解析加上形象生动的语言描述,可谓良书一本,下面就针对自己对整个消息机制的理解做个总结。

一、前言

我们都知道,Android在子线程中直接更新UI操作时,会报出异常

android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.


意思就是说,只有原始的线程(主线程/ui线程)才能修改view对象。在子线程中修改view的显示状态,会报上面的异常

这是为什么呢?

因为在Android系统中UI主线程要负责执行UI的渲染、View的绘制,这些操作都需要非常高的时效性,以保证界面不会卡顿,甚至无响应(ANR),而子线程有可能需要执行较长时间的耗时操作,比如连接网络获取数据、读取数据库获取数据等,如果把UI操作都放在子线程,则UI操作就必须等待耗时操作执行完才能绘制出来,这样便很容易引起界面无响应。

所以Android便引入了消息机制,来实现子线程和主线程之间的通信传递数据。

简单来总结一下就是:子线程获取到数据之后,不直接进行UI更新,而是把数据’装’到消息中发送到主线程,主线程中有一个循环轮询会立即收到子线程发送过来的信息,然后拿到消息数据后在主线程更新UI。

做法:通常是在主线程new一个handler,然后子线程通过handler来发送消息。最终是在handler的handleMessage方法中处理子线程发送过来的数据消息,直接进行UI更新。

那么消息机制到底是怎样运作的呢?下面开始一步步探讨

二、Handler, MessageQueue, 与 Looper之间的关系图


先来简单认识一下它们之间的关系和作用:

  • Handler 消息处理者
    它主要有两大作用:① 处理Message。② 发送Message,并将某个Message压入到MessageQueue中。

  • Looper 轮询器
    在 Looper里面的 loop()函数中有个死循环,它不断地从 MessageQueue 中取出一个Message,然后传给Handler进行处理,如此循环往复。假如队列为空,那么它会进入休眠。

  • MessageQueue 消息队列
    这个集合里面装有很多个Runnable、Message。

三、Handler 是什么?

从字面上理解,Handler的意思是:“处理者”,那么Handler处理的是什么呢?下面我们开始一步步探讨

在Android代码中,当我们在创建Handler实例,则会报如下错误:

意思就是说:在没有调用Looper.prepare()之前不能在子线程创建Handler(那么为什么不能这样?看完解析第二步马上会有答案)

先来解释一下,为什么在主线程中我们就已经可以直接创建Handler?

因为在Activity的启动代码中,已经在当前UI线程(主线程)调用了Looper.prepareMainLooper()和Looper.loop()方法。我们可以在源码的ActivityThread类中看到,如图:

从上图可以看出,在主线程中首先调用的是Looper.prepareMainLooper(),然后创建了一个ActivityThread实例,最后通过Looper类使主线程进入消息循环中。

在子线程创建handler的步骤

  • 1.Looper.prepare();
  • 2.实例化Handler,在构造方法中实现handlerMessage(Message msg)处理消息
  • 3.Looper.loop();

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
class LooperThread extends Thread {
public Handler mHandler;

public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}

我们先来看看第一步Looper.prepare(),源码做了哪些操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Looper {  
......

private static final ThreadLocal sThreadLocal = new ThreadLocal();

final MessageQueue mQueue;

......
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}

private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}


public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}

private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}

......
}

在代码第9行调用 Looper.prepare() 方法时,创建了一个 Looper 对象,这个对象是被 set 绑定到一个 ThreadLocal (线程局部变量)中:sThreadLocal.set(new Looper()); [代码第13行]

ThreadLocal的作用就是保证每一个调用了prepare()函数的线程里面都有一个唯一的Looper对象。
意思就是说:如果在一个子线程中创建一个 Handler,那么它首先调用 Looper.perpare()方法时,创建的 Looper 对象是新的,与主线程不同。

我们来看代码第33行,new Looper();

  在创建一个 Looper 对象时,同时 new 了一个MessageQueue消息队列,后续消息就是存放在这个队列中去的,这会就可以很明白知晓最开始的那张关系图了,即 Looper 中包含了一个 MessageQueue【这句话非常重要】。

第二步 我们在子线程中创建 Handler,Handler 到底是如何与 Looper 关联起来的呢?

1
2
3
4
5
public Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
...
}
};

我们在 new Handler()时,Handler 的构造函数有如下几种:

1
2
3
4
public Handler();
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);

之所以有这么多构造函数,是因为 Handler 有如下内部变量需要初始化。

1
2
3
4
5
6
7
8
9
10
11
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 时,需要获取 Looper 轮询器和 MessageQueue 消息队列,它们都作为 Handler 的成员变量,这样 Handler 和 Looper,MessageQueue 就联系起来了。

现在终于可以解释为什么在没有调用Looper.prepare()之前不能在子线程创建Handler:
因为在new Handler 的时候,首先先要有 Looper 轮询器对象(在Handler底层中是通过Looper.myLooper()获取),然后在 new Looper 轮询器的同时new MessageQueue,然后Handler在底层中再通过Looper对象的成员属性mQueue获取到MessageQueue。这样在 Handler 的构造函数中才能获得 looper和 messageQueue

第三步 Looper.loop() 轮询器是怎么轮询的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
...
msg.target.dispatchMessage(msg);
...
}
}

上面代码比较多,我们就看一句关键的代码位于第11行: msg.target.dispatchMessage(msg);

从上面代码也可以看出,轮询器在for (;;){}死循环代码块中不断的执行, 通过 queue.next();从 MessageQueue 中取出一个 Message,当msg不为空时,执行msg.target.dispatchMessage(msg);(实际上最终是调用到了Handler的dispatchMessage方法去拦截消息

在Message 中的源码中,target 就是当前线程的 Handler对象,msg的成员变量target是在发送消息的时候设置好的,一般就通过哪个Handler来发送消息,就通过哪个Handler来处理消息。

接下来我们来看 Handler 中的dispatchMessage(msg)方法

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

继续调用 handleMessage(msg);

1
2
3
4
5
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

意思就是说:每次我们 new Handler()的时候必须实现handleMessage(msg)方法才能接收到message。

现在我们可以得出一个结论:那就是 Handler 最终是通过handleMessage(msg)来处理消息

那么 Message 消息是由谁发送的呢?

我们一般在子线程中通过handler.sendMeaaage(msg)来发送消息,由于在子线程中禁止更新 UI,所以我们可以通过子线程中处理获得的数据,通过 handler 发送出去,这样数据便传递到了主线程,最终回到 handleMessage(msg)这个方法中对消息进行处理。

我们来看 Handler 的源码:
handler.sendMessage(msg)最终调用的是sendMessageAtTime

1
2
3
4
5
6
7
8
9
10
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

从上面的代码可以看出:handler最终是把 msg 压到了 MessageQueue 中。

这个时候在MessageQueue消息队列中,就有了 Message 消息,由于 Looper 是死循环的执行 loop()方法不断的轮询从MessageQueue中取出 message,最终又把 message 传递到了在主线程的 handler 让他去执行handleMessage(msg)来处理一开始从子线程发过来的 message。

结论:Handler 还负责将某个消息压入 MessageQueue 中(发消息)

Android Handler消息机制 Android源码解析