Android Handler 源码解析
本文以 API-33 为参考
Handler的初始化
Handler对象拥有数个初始化方法,但需要的参数只有三个:Looper、Callback 以及 async。
- Looper:将 message 发送到哪一个 Looper,由于 Looper 对象与线程绑定,可以理解为选择任务由哪一个线程来执行
- Callback:是 Handler 类中的一个接口,传入该对象,可以自定义处理 Message 的代码
- async:bool 值,含有该参数的构造方法是 hide 方法,也就是说,应用层正常情况下无法调用的。可以参照这篇文章:www.jianshu.com/p/5c399a27d…
Handler的初始化可以分为两个地方:子线程与主线程。初始化方式并不一样,以代码为例。
- 主线程:
// ....
Handler handler = new Handler(getMainLooper());
// ....
- 子线程:
private MyThread childThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
//...
childThread = new MyThread();
childThread.start();
Handler childHandler = new Handler(childThread.childLooper);
//...
}
private class MyThread extends Thread{
public Looper childLooper;
@Override
public void run() {
Looper.prepare();//创建与当前线程相关的Looper
childLooper = Looper.myLooper();//获取当前线程的Looper对象
Looper.loop();//调用此方法,消息才会循环处理
}
}
对比之下可以看到,在子线程中,必须要调用 Looper.prepare() 与 Looper.loop() 方法。根据上面的构造方法,我们知道Looper对象是 Handler 构造时需要的参数之一。我们来看两个方法的源码。
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");
}
// 在threadLocal中,设置一个 Looper 对象
sThreadLocal.set(new Looper(quitAllowed));
}
public static void loop() {
final Looper me = myLooper();
// 进入循环,持续处理message
for (;;) {
// loopOnce 方法中实际处理 message
// 这里需要注意,当 message 被处理完之后,就会返回 false ,退出 looper 循环
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
这里先介绍一下 ThreadLocal 对象。ThreadLocal 与线程绑定,每个线程都有一个 ThreadLocal 对象。而在 prepare 方法中,是将 Looper 对象保存在 ThreadLocal 中,这代表,Looper 是与线程绑定的,而Handler 是与 Looper 绑定的。所以,Handler 与 Looper 所在的线程绑定。而 loop 方法,代表着Looper 开始工作。
根据代码阅读,我们已经知道了,Handler 需要一个 Looper 对象,但是,主线程中,我们没有做任何类似prepare 的操作,那么,主线程中,Looper 对象是何时初始化的呢?
主线程对应的对象为 ActivityThread,答案自然在 ActivityThread 对象中。在这里,也可以看到 Java 程序中大家熟悉的 main 方法。
public static void main(String[] args) {
AndroidOs.install();
// ....
Looper.prepareMainLooper();
// ....
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// ....
// Looper 进入循环
Looper.loop();
}
以上,我们可以得出结论,主线程中,在源码中已经调用了 Looper.loop() 方法,而在子线程中,需要我们手动去调用。
Handler 发送 Message
Handler 发送 Message 有很多个方法。但最终,都会调用 SendMessageAtTime 方法,具体的调用路线以及post类方法,建议阅读源码查看。
public boolean sendMessageAtTime(@NonNull 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 是使用 enqueueMessage 方法,将 Message 对象塞进了一个队列 MessageQueue 中。这个 MessageQueue 是什么,又是从哪里来呢?
一个持有由 Looper 分发的 Messages 链表的 low-class 。Message 不会被直接添加到 MessageQueue 中,而是会通过与 Looper 绑定的 Handler 发送。
以上是源码中对 MessageQueue 的描述。根据这个描述,我们可以猜测,MessageQueue 由 Looper 对象持有,并在 Handler 初始化时,赋予给 Handler,是 Handler 将收到的 Message 通过 MessageQueue 发送给对应的 Looper 对象。在源码中,也可以印证我们的猜测。
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
// Looper 的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
言归正传,我们返回 enqueueMessage 方法。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
至此,Message 对象已经传入到 MessageQueue 中。这里需要注意的是,Handler 将自身作为参数放进了 Message 中。
Message 的分发与处理
Looper,经过之前的阅读,我们已经知道 Looper 在自身不断进行这一个循环。那么,Message 是如何从 Queue 中到 Loop 中呢?这之间,我们设置的 Delay 参数又是如何被处理的?
我们进入 MessageQueue 的 enqueueMessage 方法。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
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 {
// 在队列中插入传入的 Msg ,按照 when 参数排序
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
// 按照 when 参数排序
if (p == null || when < p.when) {
break;
}
// 消息屏障机制,后文会详解该机制
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// Native 方法,最后会调用到 Native层Looper.wake() 方法,唤醒 Looper
// 这里涉及到 Looper 的阻塞与唤醒机制
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在 enqueueMessage 中,保证了 Looper 当时处于工作状态。这里涉及到Looper的阻塞以及唤醒机制,已经深入到了 Native 层,以后有机会会全面阐述。Looper 中对于 Message 处理,则在 LoopOnce 方法中。
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
// .....
Object token = null;
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 还记得这里嘛?msg对象的 target 参数,是哪个对象?
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
// .....
} finally {
// .....
}
msg.recycleUnchecked();
return true;
}
还记得我们在 Handler 对象中,将 Message 入队时,传入的参数嘛?没错,Message 对象的 target 参数,就是发送这个 Message 的 Handler 对象。
最后,来看下 Handler 对象的 dispatchMessage 方法。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
// 优先执行 Message 中的 Callback 对象
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
同步屏障机制
同步屏障机制是一种使得异步消息被优先快速执行,屏蔽同步消息的机制
在整个Handler机制中,并不是只有一种消息,而是有三种消息类型。
- 同步消息
- 异步消息
- 同步屏障消息
我们之前在 Handler 的构造方法中,讲到了一个参数 async ,使用这个参数的构造方法,全部都被 hide 修饰 (API 28之后,可以使用 createAsync 方法),但是我们依然可以通过反射调用。当我们使用 async 为 true 构建了一个 Handler,这个 Handler 发送的消息,就是异步消息 。
而同步屏障消息,就是激活同步屏障机制,使得异步消息被优先处理。那么,如何发送同步屏障消息,同步屏障机制又是如何实现的呢?
发送同步屏障消息
同步屏障的发送,并不像其他类型的消息,在 Handler 中,而是在 Message Queue 中。
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
synchronized (this) {
// 这里是重点,传入了一个 Token
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
// .....
return token;
}
}
可以看到,这里最大的不同,是传入了一个 token 参数,并且,Message 对象没有传入 target 参数。
同步消息屏障机制的实现
要了解同步消息屏障机制的实现,需要先回忆下 Looper 中 LoopOnce 的操作。在方法中,首先调用了 MessageQueue 的 next() 方法。屏障机制的实现,主要就是在这个方法中实现的。与 Looper 实现了解耦,Looper 不负责各种消息的分辨,只是简单的分配消息。
Message next() {
// ....
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// ....
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
}
// ....
}
核心代码如上所示,可能会产生疑惑,这也没有用到 token ,是如何判断屏障机制被激活的?这里的核心在于 msg.target == null 。之前讲过,只有同步屏障消息的target 为空。这里,就是在遍历整个链表,返回第一个异步消息。
这就达到了,屏蔽同步消息,优先处理异步消息的目的。