Handler源码解析

参考资料:
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper
Android 消息机制——你真的了解Handler?
Android 消息处理机制(Looper、Handler、MessageQueue,Message)
前段时间找工作的过程中, handler消息机制的原理也被问到了好多次. 虽然面试时这个问题基本已经被问烂了, 但是它真的很有意思, 也很能考验一个人深入源码和独立思考的能力.类似的面试题还有主线程怎么给子线程发送消息, 怎么自己实现一个类似handler的东西.这些问题一会儿来讲, 先一步步说清楚handler的原理.

Looper

为什么是先分析Looper? 这个我稍候再讲, 先讲Looper的原理.
我把Looper这个类从源码里拷贝了出来放在assets目录下, 这样有多个好处, 一是方便我对这个文件添加注释和修改, 二是能够利用ide更便捷的查看源码, 同时不影响demo的编译

先来看一些Looper这个类的结构

主要是prepare(), looper()和quit()这三个方法
先看prepare()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 /** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}

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.
这给你了一次机会来创建handler.
handlers在真正开始循环之前会引用这个looper.
确保在调用这个方法之后调用loop()方法.
如果要终止它就调用quit()方法.

prepare()调用的是prepare(true)方法, quitAllowed为true表示允许退出
这里的sThreadLocal是一个ThreadLocal对象, 那么什么是ThreadLocal呢, 以下引用自《Android开发艺术探索》

ThreadLocal是一个线程内部的数据储存类, 通过它可以在指定的线程中存储数据, 数据存储以后, 只有在指定线程中可以获取到存储的数据, 对于其他线程老说则无法获取到数据. 在日常开发中用到ThreadLocal的地方较少, 但是在某些特殊的场景下, 通过ThreadLocal可以轻松地实现一些看起来很复杂的功能, 这一点在Android的源码中也有所体现, 比如Looper、ActivityThread以及AMS中都用到了ThreadLocal.具体到ThreadLocal的使用场景, 这个不好统一来描述, 一般来说, 当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候, 就可以考虑采用ThreadLocal. 比如对于Handler来说, 它需要获取当前线程的Looper, 很显然Looper的作用域就是线程并且不同线程具有不同的Looper, 这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取. 如果不采用ThreadLocal, 那么系统就必须提供一个全局的哈希表供Handler查找指定线程的Looper, 这样一来就必须提供一个类似于LooperManager的类了, 但是系统并没有这么做而是选择了ThreadLocal, 这就是ThreadLocal的好处.

show me the code. 写个测试看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ThreadLocalTest {

private static ThreadLocal<Boolean> sBooleanThreadLocal = new ThreadLocal<>();

public static void main(String[] args) {
System.out.println("[Thread#main]sBooleanThreadLocal = " + sBooleanThreadLocal.get());
sBooleanThreadLocal.set(true);
System.out.println("[Thread#main]sBooleanThreadLocal = " + sBooleanThreadLocal.get());
new Thread("Thread#1") {
@Override
public void run() {
sBooleanThreadLocal.set(false);
System.out.println("[Thread#1]sBooleanThreadLocal = " + sBooleanThreadLocal.get());
}
}.start();
new Thread("Thread#2") {
@Override
public void run() {
sBooleanThreadLocal.set(true);
System.out.println("[Thread#2]sBooleanThreadLocal = " + sBooleanThreadLocal.get());
}
}.start();
}
}


从结果可以看出虽然是调用同一个ThreadLocal对象的get()方法, 但是在不同的线程中结果却是不一样的.
现在回到prepare(true)方法,

1
2
3
4
5
6
7
8
9
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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实例放入了ThreadLocal, 以后只要在这个线程调用sThread.get()都会返回这个放进去的Looper实例. 在存放之前还会判断sThreadLocal是否有了Looper实例, 如果已经有了, 说明之前已经调用过了prepare()方法, 会抛出异常, 可见prepare()方法不能被调用两次, 也就保证了一个线程中只有一个Looper实例.
接下来看Looper的构造方法

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

在构造方法中, 创建了一个MessageQueue赋值给mQueue.
接下来看看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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
// 取出刚才prepare()方法中存储在sThreadLocal中的Looper实例
final Looper me = myLooper();
// 如果取出的Looper实例为null, 说明在该线程中没有调用prepare()方法, 所以抛出异常要求先调用prepare()方法
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 获取Looper构造方法中new出来的那个MessageQueue
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, 这个过程也许会阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
// 没有message意味着这个消息队列正在退出, 那就直接跳出循环
return;
}

// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
// 打印一下这个Message分发给的handler, 它的callback, 和它的what
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}

final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
// 让这个msg的目标handler去处理消息
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}

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.
// 确保在分发和处理消息的过程中线程的id没有损坏
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();
}
}

必要的注释我大致在代码中写清楚了.
final Looper me = myLooper()这一句会取出刚才在sThreadLocal中存储的Looper实例

1
2
3
4
5
6
7
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

然后进入了一个无线循环.
这个无限循环中一次完整的过程是这样的:
取出MessageQueue中的下一个Message消息, 如果这个Message不为null, 那就调用msg.target.dispatchMessage(msg)方法, 让这个msg的目标handler去处理消息, 最后调用msg.recycleUnchecked()来回收这个消息.
然后进行下一次过程, 周而复始.
Looper的主要作用:

  1. 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue, 也意味着一个线程只有一个MessageQueue
  2. loop()方法不断从MessageQueue中去取消息,交给消息的目标handler去处理

    Handler

    先看一下Handler的结构

    我们写handler一般都是在主线程的代码中private Handler mHandler = new Handler();类似这样的, 所以首先来看一下Handler的构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public Handler() {
this(null, false);
}

public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

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实例, 记住这个Looper是和该线程关联的. 然后将这个Looper的MessageQueue赋值给handler的全局变量mQueue. 如果取出的mLooper为null就抛出异常, 要求先调用Looper.prepare()方法. 相信很多人都遇到过这个错误, 最常见的错误做法就是在新线程中直接new一个Handler.
然后看一下我们最常用的sendMessage(Message)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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

最终调用的是sendMessageAtTime(Message, long)方法, 这个方法内部调用enqueueMessage(MessageQueue, Message, long)方法, 那我们再去看一下这个方法

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

首先为msg.target赋值为this, 也就是说这个Message的target现在指向这个handler了, 然后调用MessageQueue的enqueueMessage(Message, long)方法, 那么这个方法是什么意思呢?可以先不去看它的源码, 因为有点难. 看英文的字面意思应该是将这个Message插入队列的意思, 先记住这点就行了. 也就是说handler发出的消息, 最终会保存到MessageQueue中
现在再把上面的流程串联起来梳理一遍, Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息, 然后调用msg.target.dispatchMessage(msg)方法, 也就是使用handler来处理这个消息, 那我们再去看看这个方法

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

可以看到, 如果msg.callback和handler本身的mCallback都为null, 就会调用handler的handlerMessage(Message)方法.
而我们在写一个new一个handler的时候往往都会重写handler的handlerMessage(Message)方法, 在方法体内写上我们自己的业务逻辑, 来处理子线程过程结束之后需要完成的事.
另外可以看出, 要处理子线程结束之后的逻辑, 我们也可以把逻辑代码写在Message的callback里或者在new Handler(Callback)的时候写在Callback里.
到此整个流程已经基本清楚.

handler.post(Runnable r)

我们在看一下我们偶尔用到的post(Runnable)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

可以看到, 这个方法其实是调用了sendMessageDelayed(Message, long)方法, 不同的是在对传入进去的Runnable进行了包装, 使用一个Message, 让这个Message的callback指向了这个Runnable. 最终调用的是和handler.sendMessage(Message)方法一样的.
刚才我们已经看过, handler.dispatchMessage(Message)的时候, msg.callback如果不为null, 那么就会执行handleCallback(msg)方法处理msg.callback中的逻辑

总结

最后总结一下handler是如何完成线程间的通信的, 以子线程发消息给主线程为例:

  1. 首先主线程中会调用Looper.prepare()在主线程中保存一个Looper实例, 然后在该Looper实例中保存一个MessageQueue对象, 这样这个MessageQueue就和主线程进行了绑定
  2. Looper.loop()让当前线程进入一个无限循环, 不断从MessageQueue中读取消息
  3. 现在我们在主线程new一个handler, 在这个handler的构造方法中会得到当前线程保存的Looper实例, 以及Looper实例中的MessageQueue, 这个时候handler, MessageQueue和主线程这三者都已经绑定在一起了
  4. 现在我们起一个子线程, 在子线程中做一些耗时操作, 结束之后调用刚才的那个和主线程绑定的handler的sendMessage(Message)方法发送一个消息, 这个方法会将Message的target指向handler自身, 然后将这个Message加入到MessageQueue中
  5. 上面说了, Looper.loop()在for循环中不断的从MessageQueue中读取消息, 那么它就一定能读取到刚才加入MessageQueue中的消息. 一旦收到了消息就意味着完成了线程间的通信. 然后调用msg.target.dispatchMessage(msg)方法去处理这个消息.

需要注意的是在Looper.loop()的无线循环中, Message msg = queue.next()这一句是”阻塞”在那里的, 如果取到了下一个Message就会往下执行. 这里其实还有好多谜团没有解开, 主要是MessageQueue的next()enqueueMessage(Message, long)方法

github地址

https://github.com/mundane799699/AndroidProjects/tree/master/HandlerExplore

0%