介紹
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
,感興趣的朋友可以看看源碼;