整个Handler源码分析分为两部分
Handler源码分析(同步屏障、异步消息和IdleHandler)
上一篇我们已经分析完了整体的基础流程,这一篇来分析一下同步屏障、异步消息以及IdleHandler。
消息分类
首先我们先来了解一下Message的种类。 Message 可以分为三种:普通消息(同步消息)、屏障消息和异步消息。
同步消息
我们平时默认使用的都是同步消息,即 Handler里的构造函数参数 async 默认为false。同步消息是按照 msg.when 来排的
异步消息
如果我们在创建Handler时 async 为 true,那么 这个handler发送的消息都是 异步消息。或者是在发送Message时,通过 msg.setAsynchronous(true) 来设置消息为异步消息。
异步消息需要配合屏障消息才有效果,否则跟同步消息一样
屏障消息(Barrier)
屏障消息是一种特殊的Message,它最大的特征是 target是null,且 arg1 属性被用作屏障的标识符来区别不同的屏障。 屏障的作用是用来拦截队列中的同步消息,放行异步消息。
只有屏障消息的target可以为null,如果我们自己设置 Message的target 为null,会抛出异常,在 MessageQueue 的 enqueueMessage 方法中我们可以看到 如果 msg.target为null 会抛出异常。
屏障消息的添加和删除
添加
kotlin
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
// 屏障消息的token,作为唯一标识,用于移除屏障消息
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
// 标记为正在使用,并设置 when 和 arg1
msg.markInUse();
msg.when = when;
msg.arg1 = token;
// 将屏障消息按时间排序插入到消息队列中
Message prev = null;
Message p = mMessages;
// 找到两个相邻的消息,满足 prev.when < msg.when < p.when
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
// 将屏障消息插入到 prev 和 p之间
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
移除
kotlin
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
// 通过 token 和 target 来找到需要删除的屏障消息
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
// 上面找到屏障消息的指针p后,把前一个消息指向屏障消息的后一个消息,这样就把屏障消息移除了
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
// 回收 p
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
屏障消息的作用
让我们来看 MessageQueue 的next方法中
kotlin
// 判断msg 是否是屏障消息
// 如果是屏障消息,找到屏障消息后边的异步消息,并执行,阻塞同步消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
// 这里的isAsynchronous方法就是前面设置进msg的async参数,通过它判断如果是异步消息,则跳出循环,把该异步消息返回
// 否则是同步消息。
} while (msg != null && !msg.isAsynchronous());
}
从上面的代码我们可以知道,如果当前消息是屏障消息,那么后续会阻塞同步消息,优先执行异步消息。
实际应用
ViewRootImpl.java
kotlin
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 发送屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 在这个地方会有发送异步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除屏障消息
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
这是View UI界面更新的逻辑,通过同步屏障+异步消息可以保证消息被优先处理,保证界面的迅速更新。
IdleHandler
IdleHandler 实际上是一个接口,在消息循环空闲的时候(没有消息)进行的回调方法。接口方法的返回值代表是否需要移除当前 IdleHandler。
kotlin
public static interface IdleHandler {
boolean queueIdle();
}
添加/移除 IdleHandler
kotlin
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<>();
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
执行 IdleHandler
kotlin
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
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());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
// pendingIdleHandlerCount < 0是说for循环只执行第一次
// mMessages == null || now < mMessages.when) 是说当前消息队列没有消息或者要执行的消息晚于当前时间
// 说明现在消息队列处于空闲。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
// 获取 IdleHandler的个数
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
// 执行 IdleHandler
// 如果queueIdle返回true,则该空闲消息不会被自动删除,在下次执行next的时候,如果还出现队列空闲,会再次执行。
// 如果返回false,则该空闲消息会在执行完后,被自动删除掉。
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
// 这里把空闲消息标志置为0,而不置为-1,就是说本次已经处理完,防止for循环反复执行,影响其他消息的执行
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
从本质上来说,IdleHandler 的作用就是趁着消息队列空闲的时候干点事情,具体干啥,就要看 IdleHandler 的 queueIdle() 方法了。
实际应用
在ActivityThread中有一个GCIdleHandler,用于做GC的
kotlin
class H extends Handler {
......
public static final int GC_WHEN_IDLE = 120;
......
public void handleMessage(Message msg) {
switch (msg.what) {
......
case GC_WHEN_IDLE:
scheduleGcIdler();
break;
......
}
}
}
当收到 GC_WHEN_IDLE 消息后,就会触发 scheduleGcIdler()
kotlin
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
void unscheduleGcIdler() {
if (mGcIdlerScheduled) {
mGcIdlerScheduled = false;
Looper.myQueue().removeIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
purgePendingResources();
return false;
}
}
void doGcIfNeeded() {
doGcIfNeeded("bg");
}
void doGcIfNeeded(String reason) {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
//Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime()
// + "m now=" + now);
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
//Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
BinderInternal.forceGc(reason);
}
}
从上边的代码我们可以看出 线程空闲时会调用 mGCIdler,最终调用BinderInternal.forceGc(reason); 触发 GC,最小间隔是5秒。需要注意的是,合格 queueIdle 返回的false,所以会长期存在于主线程的 MessageQueue中。