拋出
Handler
中的IdleHandler
- 它有什么能力?
- 它有什么用處?
- 能想到一些合適的場景嗎?
Answer1:
首先看下源碼的注釋
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
注釋中明確的指出當消息隊列空閑時會執行IdelHandler
的queueIdle()
方法,該方法返回一個boolean
值,
如果為false
則執行完畢之后移除這條消息,
如果為true
則保留,等到下次空閑時會再次執行,
下面看下MessageQueue
的next()
方法可以發現確實是這樣。
Message next() {
......
for (;;) {
......
synchronized (this) {
// 此處為正常消息隊列的處理
......
if (mQuitting) {
dispose();
return null;
}
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);
}
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);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
處理完IdleHandler
后會將nextPollTimeoutMillis
設置為0
,也就是不阻塞消息隊列, 當然要注意這里執行的代碼同樣不能太耗時,因為它是同步執行的,如果太耗時肯定會影響后面的message
執行。
能力大概就是上面講的這樣,那么能力決定用處,用處從本質上講就是趁著消息隊列空閑的時候干點事情,當然具體用處還是要看具體的處理。
要使用IdleHandler
只需要調用MessageQueue#addIdleHandler(IdleHandler handler)
方法即可
合適場景可以從下面幾點出發:
- 消息隊列相關
- 主線程能干的事情
- 返回true和false帶來不同結果
目前可以想到的場景:
1.Activity
啟動優化:onCreate,onStart,onResume
中耗時較短但非必要的代碼可以放到IdleHandler
中執行,減少啟動時間
2.想要一個View繪制完成之后添加其他依賴于這個View
的View
,當然這個View#post()
也能實現,區別就是前者會在消息隊列空閑時執行。
3.發生一個返回true
的IdleHandler
,在里面讓某個View
不停閃爍,這樣當用戶發呆時就可以誘導用戶點擊這個View
,這也是種很酷的操作。
4.一些第三方庫中使用,比如LeakCanary,Glide中使用到,具體可以自行去查看。
Answer2:
這個IdleHandler
,它是聲明在MessageQueue
里面的一個接口,所以我們可以猜想到它跟MessageQueue
一定有關系,這個接口只有一個方法:queueIdle
,字面意思就是隊列空閑。
看MessageQueue
的源碼可以發現有兩處關于IdleHandler
的聲明,分別是:
- 存放
IdleHandler
的ArrayList(mIdleHandlers)
- 還有一個
IdleHandler
數組(mPendingIdleHandlers)
后面的數組,它里面放的IdleHandler
實例都是臨時的,也就是每次使用完(調用了queueIdle
方法)之后,都會置空(mPendingIdleHandlers[i]=null)
那它們會在什么時候用到呢?
就是在MessageQueue
的next
方法里面
大概流程是這樣的:
1.如果本次循環拿到的Message
為空,或者!這個Message
是一個延時的消息而且還沒到指定觸發時間,那么就認定當前的隊列為空閑時間。
2.接著會遍歷mPendingIdleHandlers
數組(這個數組里面的元素每次都會到mIdleHandlers中去拿)來調用每一個IdleHandler
實例的queueIdle
方法。
3.如果這個方法返回false
的話,那么這個實例就會從mIdleHandlers
中移除,也就是當下次隊列空閑的時候,不會繼續回調它的queueIdle方法了。
來看看它在源碼里的使用場景:
比如在ActivityThread
中,就有一個名叫GcIdler
的內部類,實現了IdleHandler
接口。
它在queueIdle
方法被回調時,會做強行GC
的操作(即調用BinderInternal
的faceGc
方法),但強行GC
的前提是與上一次強行GC
至少相隔5秒以上。
那這個GcIdler會在什么時候使用呢?
當ActivityThread
的mH
(Handler)收到GC_WHEN_IDLE
消息之后。
何時會收到GC_WHEN_IDLE
消息?
當AMS
(ActivityManagerService)中的這兩個方法被調用之后:
-
doLowMemReportIfNeededLocked
,這個方法看名字就知道是不夠內存的時候調用了。 -
activityIdle
,這個方法呢,就是當ActivityThread
的handleResumeActivity
方法被調用時(Activity
的onResume
方法也是在這方法里面回調)調用的。
Answer3:
IdleHandler
:空閑監聽器(就好像我沒事做了,在群里發了個表情,這時候其他人就知道我很閑了)
在每次next獲取消息進行處理時,發現沒得可以處理的消息(隊列空,只有延時消息并且沒到時間,同步阻塞時沒有異步消息)都會通知這些訂閱者。
適合做一些可有可無的東西,因為這個通知太不穩定了(就像別人說過幾天請你吃飯,你要真傻等著估計能餓死)
Answer4:
去看了MessageQueue
的next
方法的源碼,是在MessageQueue
空閑(當MQ中Message
消息全部執行完畢或者處理一個延時消息)時調用。
系統還在這幾個地方應用了:
-
Activity onResume
以及view
初始化完成之后調用 - 強行
GC
,當內存不足時調用 -
TTS
合成之后發生廣播 - 在
Activity onCreate()
執行前添加 - 在鍵盤相關調用
結合系統的使用場景,推測一下,可以做UI操作,不能太耗時,調用的時機比較模糊關聯性不能太強。