介绍
IdleHandler
是 MessageQueue
内定义的一个接口,一般可用于做性能优化。当消息队列内没有需要立即执行的 message
时,会主动触发 IdleHandler
的 queueIdle
方法。返回值为 false,即只会执行一次;返回值为 true,即每次当消息队列内没有需要立即执行的消息时,都会触发该方法。
public final class MessageQueue {
public static interface IdleHandler {
boolean queueIdle();
}
}
使用方式
通过获取 looper
对应的 MessageQueue
队列注册监听。
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// doSomething()
return false;
}
});
源码解析
IdleHandler
的执行源码很短。
Message next() {
// 隐藏无关代码...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (; ; ) {
// 隐藏无关代码...
// 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.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
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 {
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.
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;
}
- 在
MessageQueue
里next
方法的for
死循环内,获取mIdleHandlers
的数量pendingIdleHandlerCount
; - 通过
mMessages == null || now < mMessages.when
判断当前消息队列为空或者目前没有需要执行的消息时,给pendingIdleHandlerCount
赋值; - 当数量大于 0,遍历取出数组内的
IdleHandler
,执行queueIdle()
; - 返回值为
false
时,主动移除监听mIdleHandlers.remove(idler)
;
使用场景
- 如果启动的
Activity
、Fragment
、Dialog
内含有大量数据和视图的加载,导致首次打开时动画切换卡顿或者一瞬间白屏,可将部分加载逻辑放到queueIdle()
内处理。例如引导图的加载和弹窗提示等; - 系统源码中
ActivityThread
的GcIdler
,在某些场景等待消息队列暂时空闲时会尝试执行 GC 操作; - 系统源码中
ActivityThread
的Idler
,在handleResumeActivity()
方法内会注册Idler()
,等待handleResumeActivity
后视图绘制完成,消息队列暂时空闲时再调用AMS
的activityIdle
方法,检查页面的生命周期状态,触发activity
的stop
生命周期等。
这也是为什么我们BActivity
跳转CActivity
时,BActivity
生命周期的onStop()
会在CActivity
的onResume()
后。 - 一些第三方框架
Glide
和LeakCanary
等也使用到IdleHandler
,感兴趣的朋友可以看看源码;