本文尝试对Handler及其相关组件在Java层进行简单分析。

最初学习Android开发时,经常会碰到使用Handler切换线程来更新UI的代码。而现在,大家似乎更喜欢用其它方式去实现多线程的切换。即便如此,作为Android Framework架构的一个基础控件,Handler机制依旧有其学习价值。

Handler由以下部分组成:

它们之间的运作方式可以以可视化的方式展示为:

接下来会对各个组件进行分析。

Looper

Looper 是在线程内维持 Message 循环的一个类。

我们一般在主线程里声明 Handler,而有时我们需要在其它线程里声明 Handler,若与在主线程里一样直接声明的话会抛异常,原因是主线程(ActivityThread)已经调用了 Looper 的prepareMainLooper()loop()方法。(实际开发中用HandlerThread即可,没有必要自己创建关联 Looper 的线程)

我们来分析 Looper 的两个主要方法prepare()loop()

  • Looper.prepare()
1
2
3
4
5
6
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));
}

可以看出,每个线程只能绑定一个Looper实例,prepare()方法通过ThreadLocal实现 Looper 实例与线程间的绑定,
通过观察 Looper 构造函数:

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

可以得知,在 Looper 实例化时会创建一个MessageQueue对象以及持有当前线程的引用。

  • 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
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(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

...

final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

...

msg.recycleUnchecked();
}
}

Looper 对象不能为空,也即loop()方法的调用必须放在prepare()方法后。

loop()方法里,Looper 会不断地MessageQueue.next()方法从 MessageQueue 对象里的取出 Message,然后通过代码msg.target.dispatchMessage(msg)让 Message 所绑定的 Handler 执行dispatchMessage()方法来处理 Message。

Handler

Handler 可以在线程绑定的 MessageQueue 中传递和处理 Message。

Handler 有多个构造函数,篇幅所限,这里只分析默认构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}

构造方法里获取了线程绑定的 Looper 实例,又通过该 Looper 实例获取了其中的 MessageQueue 实例。

同一线程里的多个Handler实例分享同一个 MessageQueue 实例,因为他们分享同一个 Looper 实例。

一般我们通过调用方法sendMessage()及其变式来把 Message 添加到 MessageQueue的末尾,据分析方法源码可知,无论是sendMessage()还是post()其最终调用的都是Handler.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);
}

从代码可以得知上面 Looper 里的 loop()方法里代码msg.target.dispatchMessage(msg)中的 target 即是传递 Message 时绑定的 Handler 对象。

再来看看dispatchMessage()这个方法:

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);
}
}

可以看到 Handler 有三种方式来处理 Message,依次为:

  1. 由用Handler.post()方式传递 Runnable 包装成的 Message 里的 Runnable 自行处理。
  2. 由 Handler 构造函数传入的 Callback 处理。
  3. 由 Handler 的handleMessage()方法处理。

Message

Message 是容纳任意数据的容器。

Message 一般通过调用obtain()或者obtainMessage()方法来获取实例,因为 Message 里维护了一个存储了 Message 对象最大值为50的 LinkedList 集合。

在 Looper 的loop()方法里,当 Handler 完成对 Message 的处理后,会调用Message.recycleUnchecked()方法来重置 Message。

MessageQueue

MessageQueue 是一个将被 Looper 分发的 Message 链表集合。

上面 Handler 里提到的将 Message 添加到 MessageQueue 的 Handler.enqueueMessage()方法最终调用的是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
47
48
49
50
51
52
53
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;
}

以较为直观的层次(不涉及native层方法)来解释上面的逻辑:

  • 判断 Message 是否指定 Handler
  • 判断 Message 是否已经插入到链表中
    • 判断 MessageQueue 是否在退出状态
      • 退出状态抛出异常,回收 Message
      • 非退出状态
        • 把 Message 标记为已插入,为 Message 附上入参的时间戳
        • 判断 Message 相对链表的位置
          • 置于链表头部
          • 比较并置于链表相应位置里

水平所限,MessageQueue 其它部分读不懂,故先分析到这里。

内存泄漏

当直接在主线程里直接声明 Handler 时,IDE 的 Lint 提醒代码可能会造成内存泄漏。

泄漏的原因

所有使用 Handler 发送的 Message 都会调用到 Handler.enqueueMessage()方法,正如上面分析的,在该方法里,Handler 的引用被显式地赋予了msg.target,用于 Looper 对应 MessageQueue 里的 Message 从 MessageQueue 出列时能选择相对应的 Handler 进行处理。

Message 添加到 MessageQueue 后,MessageQueue 就持有了 Message 的引用。而 MessageQueue 又被跟程序有相同生命时长的主线程 Looper 所引用。因此,Message 中持有的 Handler 会一直持续到 Message 被 MessageQueue 回收。

虽然 Handler 有很长的存活时间,但不能确定 Activity 是否发生了泄漏。要检查是否发生了泄漏,我们需要确定 Handler 是否在类里持有了 Activity 的引用。若 Handler 是以一个非静态内部类的形式声明于类中,那么便会发生泄漏,因为非静态内部类对其外部类持有一个隐式引用。

解决泄漏

通过组合使用弱引用和静态修饰符可防止 Activity 内存泄漏。当 Activity 被销毁时,弱引用会让 GC 能回收你依旧持有的 Activity 引用。而在 Handler 内部类前添加静态修饰符可以避免持有外部类的隐式引用。

由于应用了弱引用,在使用 Actvity 引用时要格外小心,因为有可能已经被 GC 回收了,不妨先加个判断条件:

1
2
3
4
5
if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
// 清空 callbacks 和 messages.
removeCallbacksAndMessages(null);
return;
}

Reference