Android的Handler机制

首先放一张流程图:

上面这张流程图主要涉及了Handler通讯的四个组成部分: Handler、Message、Looper、MessageQueue, 下面我们就从message4的传递过程来解释Handler的机制.

Message

从图中可以看到, 在创建message4实例的时候, 我们并没有调用构造函数, 而是使用了一个类方法. 这是为什么呢?我们看一下源码:

Message
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;

private static final int MAX_POOL_SIZE = 10;

/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}

源码还是比较好懂的, 简单来说, Message使用了一个消息池来循环利用这些message, 如果在申请message的时候池子里没有了, 才会新创建一个message. 当然, 我们也可以主动调用Message的构造函数.

Handler

message创建完后, 就要调用handler的sendMessage函数了. 不过在调用之前, 我们肯定得先创建一个handler对吧?那我们就得先看看handler的构造函数了:

Handler
1
2
3
4
5
6
7
8
9
10
11
12
13
public Handler(Callback call) {
....

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;

}

函数很长, 所以这里只放出了一部分. 这部分非常重要, 无论是参数call还是handler保留的Looper和消息队列的引用都是一个大的伏笔, 我们下面会提到.

现在我们再看一下sendMessage源码:

sendMessage
1
2
3
4
5
6
7
8
9
10
11
12
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;
}
//将新来的Message加入到MessageQueue中
return enqueueMessage(queue, msg, uptimeMillis);
}

sendMessage和sendMessageAtTime逻辑一样, 所以我们这么也可以看(实际上是因为我从源码那里拿错了……). 其实这个函数并没有多少内容, 除了出错处理外, 实际上还是执行了enqueueMessage这个函数, 那么我们再看一看这个函数的源码:

enqueueMessage
1
2
3
4
5
6
7
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

看起来好像还是没写啥, 因为抛去异步处理, 其实还是调用了MessageQueue的enqueueMessage函数. 但是其实这里埋了一个伏笔. 我们关注一下第一行的赋值: message4持有了handler4的一个引用. 明明是handler4对message4进行处理呀, 为什么message4持有handler4的引用呢?先按下不表, 我们再往下看消息队列.

MessageQueue

在看消息队列的源码之前, 我们先看一下图: ThreadA有一个消息队列, 而ThreadB没有. 没错, 消息队列不是每一个线程都有的. 在默认情况下, 只有负责UI的主线程会拥有一个消息队列. 不过我们也可以人为的给一个线程创建一个消息队列, 至于怎么创建, 我们下面会提到.

然后我们再说一下消息队列的性质. 既然它的名字叫做消息队列, 那么肯定就符合先进先出的性质了. 放在Message的这个情境下, 就是说消息队列按照message创建的先后顺序管理它们, 旧的message先出队被处理, 新的message后出队被处理. 而我们都知道, 队列一般都是由数组或者链表实现的, 像消息队列这种单元很抽象的队列, 自然应该是用链表实现.

现在我们可以看源码了:

MessageQueue.enqueueMessage
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
41
42
43
44
45
46
boolean enqueueMessage(Message msg,
long when)
{
...
synchronized (this)
{
...
msg.markInUse();
msg.when = when;
//p指向消息队列头结点
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
{
...
//从消息队列队头开始寻找合适的位置将消息插入
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;
}
}
...
return true;
}

由于这个函数很长很长, 所以我只放出了相关的部分, 同时为了方便大家看, 我做了点注释. 这样大家可以看到, handler的sendMessage函数实际上就是把message放到了handler在构造时绑定的消息队列中, 就像图上那样.

Looper

光是把message入队可不行, 还必须要有出队的操作. Looper就是实现这个功能的. 关于Looper, 我们主要用到Looper.prepare和Looper.loop两个函数. 我们先看一下prepare函数:

Looper
1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

我们看到, Looper在被创建时, 会一同创建消息队列, 同时保有创造线程的引用. 再结合prepare函数中的出错处理, 我们就可以知道Looper和消息队列与线程的关系了: 除了用于更新UI的主线程, 其它线程默认是没有Looper和消息队列的, 但是我们可以人为为线程创建Looper, Looper在创建的过程中会主动创建一个消息队列. 对于一个线程来说, 它的Looper和消息队列是唯一的. 如果在拥有Looper和消息队列的情况下再调用Looper.prepare, 系统就会报错.

为什么Looper和消息队列是绑定的呢?答案在Looper.loop函数里:

Looper.loop
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
41
42
43
44
45
46
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;

// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

for (;;) {
Message msg = queue.next();
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}

msg.target.dispatchMessage(msg);

if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}

// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}

msg.recycleUnchecked();
}
}

除去各种报错函数, 其实loop函数的逻辑很简单: 一个不断执行Message msg = queue.next()、msg.target.dispatchMessage(msg)、msg.recycleUnchecked()的死循环.

Message msg = queue.next()很好理解, 就是从队列中拿出了队首的Message.

msg.recycleUnchecked()也很好理解, 用完message后就将其放回消息池内.

那么msg.target.dispatchMessage(msg)怎么理解?这就是Message要保留handler引用的原因. 我们看一下dispatchMessage函数的源码:

dispatchMessage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//处理方式1
if (msg.callback != null) {
handleCallback(msg);
} else {
//处理消息方式2
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//处理消息方式3
handleMessage(msg);
}
}

dispatchMessage函数根据情况提供了三种方式处理message. 第一种是通过handler的post函数创造一个包含方法回调的message(至于什么是方法回调, 以后我们再说); 第二种是通过Handler(Callback)的构造函数在创建handler时就把回调函数放进去, message仅仅是通知handler执行该方法的信号; 第三种就是我们常用的重写handlerMessage函数.

总结

写到这里, Handler的机制就已经说完了. 因为设计到源码的展示, 所以我只想到了上面的这种解说方式, 可能这样还是很难在脑海中构建出一个模型. 不过当大家看到这里的时候, 上面的内容也大概都有印象了, 在这里就再用文字概括一下Handler的机制:

  1. 每个线程最多只能有一个消息队列和一个Looper, 除了主线程之外, 其他线程都需要通过Looper.prepare()创建消息队列和Looper.

  2. Handler在创建时就会与创建它的线程的Looper和消息队列绑定. 在执行sendMessage时, handler会把message放到它绑定的消息队列的队尾. 这个时候handler不会执行处理message的方法.

  3. Looper就像一个引擎, 不断地通过Looper.loop将消息队列的消息取出并分发到message对应的handler那里. 这也就是我们说Android是事件驱动的的原因.

  4. handler在从Looper那里拿到分发的message后, 才会执行相应的处理函数. 处理函数一共分三种. 第一种是通过handler的post函数创建一个message, message既是通知handler执行处理函数的信号, 也是运输处理函数的载体; 第二种是将执行函数直接写入handler中, message仅作为通知handler执行的信号; 第三种是重写handler的handlerMessage函数, message既是信号, 也传输了简单的函数参数.

请大家参考上面的四点, 再对照文章开始的图片, 应该就不难理解了.