介绍
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;
}
1.在 MessageQueue 里 next 方法的 for 死循环内,获取 mIdleHandlers 的数量 pendingIdleHandlerCount;
2.通过 mMessages == null || now < mMessages.when 判断当前消息队列为空或者目前没有需要执行的消息时,给 pendingIdleHandlerCount 赋值;
3.当数量大于 0,遍历取出数组内的 IdleHandler,执行 queueIdle() ;
4.返回值为 false 时,主动移除监听 mIdleHandlers.remove(idler);
使用场景
1.如果启动的 Activity、Fragment、Dialog 内含有大量数据和视图的加载,导致首次打开时动画切换卡顿或者一瞬间白屏,可将部分加载逻辑放到 queueIdle() 内处理。例如引导图的加载和弹窗提示等;
2.系统源码中 ActivityThread 的 GcIdler,在某些场景等待消息队列暂时空闲时会尝试执行 GC 操作;
3.系统源码中 ActivityThread 的 Idler,在 handleResumeActivity() 方法内会注册 Idler(),等待 handleResumeActivity 后视图绘制完成,消息队列暂时空闲时再调用 AMS 的 activityIdle 方法,检查页面的生命周期状态,触发 activity 的 stop 生命周期等。
这也是为什么我们 BActivity 跳转 CActivity 时,BActivity 生命周期的 onStop() 会在 CActivity 的 onResume() 后。
4.一些第三方框架 Glide 和 LeakCanary 等也使用到 IdleHandler,感兴趣的朋友可以看看源码;
网友评论