Android IdleHandler 详解

2,111 阅读2分钟

介绍

IdleHandlerMessageQueue 内定义的一个接口,一般可用于做性能优化。当消息队列内没有需要立即执行的 message 时,会主动触发 IdleHandlerqueueIdle 方法。返回值为 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. MessageQueuenext 方法的 for 死循环内,获取 mIdleHandlers 的数量 pendingIdleHandlerCount
  2. 通过 mMessages == null || now < mMessages.when 判断当前消息队列为空或者目前没有需要执行的消息时,给 pendingIdleHandlerCount 赋值;
  3. 当数量大于 0,遍历取出数组内的 IdleHandler,执行 queueIdle()
  4. 返回值为 false 时,主动移除监听 mIdleHandlers.remove(idler)

使用场景

  1. 如果启动的 ActivityFragmentDialog 内含有大量数据和视图的加载,导致首次打开时动画切换卡顿或者一瞬间白屏,可将部分加载逻辑放到 queueIdle() 内处理。例如引导图的加载和弹窗提示等;
  2. 系统源码中 ActivityThreadGcIdler,在某些场景等待消息队列暂时空闲时会尝试执行 GC 操作;
  3. 系统源码中 ActivityThreadIdler,在 handleResumeActivity() 方法内会注册 Idler(),等待 handleResumeActivity 后视图绘制完成,消息队列暂时空闲时再调用 AMSactivityIdle 方法,检查页面的生命周期状态,触发 activitystop 生命周期等。这也是为什么我们 BActivity 跳转 CActivity 时,BActivity 生命周期的 onStop() 会在 CActivityonResume() 后。
  4. 一些第三方框架 GlideLeakCanary 等也使用到 IdleHandler,感兴趣的朋友可以看看源码;