介紹
IdleHandler
是 MessageQueue
內(nèi)定義的一個(gè)接口,一般可用于做性能優(yōu)化型酥。當(dāng)消息隊(duì)列內(nèi)沒(méi)有需要立即執(zhí)行的 message
時(shí)频伤,會(huì)主動(dòng)觸發(fā) IdleHandler
的 queueIdle
方法晌该。返回值為 false戒祠,即只會(huì)執(zhí)行一次;返回值為 true加酵,即每次當(dāng)消息隊(duì)列內(nèi)沒(méi)有需要立即執(zhí)行的消息時(shí)拳喻,都會(huì)觸發(fā)該方法。
public final class MessageQueue {
public static interface IdleHandler {
boolean queueIdle();
}
}
使用方式
通過(guò)獲取 looper
對(duì)應(yīng)的 MessageQueue
隊(duì)列注冊(cè)監(jiān)聽(tīng)猪腕。
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// doSomething()
return false;
}
});
源碼解析
IdleHandler
的執(zhí)行源碼很短冗澈。
Message next() {
// 隱藏?zé)o關(guān)代碼...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (; ; ) {
// 隱藏?zé)o關(guān)代碼...
// 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
死循環(huán)內(nèi),獲取mIdleHandlers
的數(shù)量pendingIdleHandlerCount
陋葡; - 通過(guò)
mMessages == null || now < mMessages.when
判斷當(dāng)前消息隊(duì)列為空或者目前沒(méi)有需要執(zhí)行的消息時(shí)亚亲,給pendingIdleHandlerCount
賦值; - 當(dāng)數(shù)量大于 0腐缤,遍歷取出數(shù)組內(nèi)的
IdleHandler
捌归,執(zhí)行queueIdle()
; - 返回值為
false
時(shí)岭粤,主動(dòng)移除監(jiān)聽(tīng)mIdleHandlers.remove(idler)
惜索;
使用場(chǎng)景
- 如果啟動(dòng)的
Activity
、Fragment
剃浇、Dialog
內(nèi)含有大量數(shù)據(jù)和視圖的加載巾兆,導(dǎo)致首次打開(kāi)時(shí)動(dòng)畫(huà)切換卡頓或者一瞬間白屏,可將部分加載邏輯放到queueIdle()
內(nèi)處理虎囚。例如引導(dǎo)圖的加載和彈窗提示等角塑; - 系統(tǒng)源碼中
ActivityThread
的GcIdler
,在某些場(chǎng)景等待消息隊(duì)列暫時(shí)空閑時(shí)會(huì)嘗試執(zhí)行 GC 操作淘讥; - 系統(tǒng)源碼中
ActivityThread
的Idler
吉拳,在handleResumeActivity()
方法內(nèi)會(huì)注冊(cè)Idler()
,等待handleResumeActivity
后視圖繪制完成适揉,消息隊(duì)列暫時(shí)空閑時(shí)再調(diào)用AMS
的activityIdle
方法,檢查頁(yè)面的生命周期狀態(tài)煤惩,觸發(fā)activity
的stop
生命周期等嫉嘀。
這也是為什么我們BActivity
跳轉(zhuǎn)CActivity
時(shí),BActivity
生命周期的onStop()
會(huì)在CActivity
的onResume()
后魄揉。 - 一些第三方框架
Glide
和LeakCanary
等也使用到IdleHandler
剪侮,感興趣的朋友可以看看源碼;