IdleHandler是在主線程空閒時執行同步任務, 即可以做優先級低的業務邏輯。
當手機拖動列表或點擊View時, MessageQueue肯定不爲空, 不會執行IdleHandler。
對於啓動新Activity可以在cmd窗口執行adb logcat -s ActivityManager | grep “Displayed” 得到時長。
用法:
public DemoActivity extends Activity {
IdleHandler mHandler1 ;
IdleHandler mHandler2;
public void onCreate(Bundle savedInstanceState) {
handler1 = new IdleOnce();
handler2 = new IdleAlways();
Looper.myQueue().addIdleHandler(handler1);
Looper.myQueue().addIdleHandler(handler2);
}
@Override
public void onDestroy() {
super.onDestroy();
Looper.myQueue.removeIdleHandler(mHandler1);
Looper.myQueue.removeIdleHandler(mHanlder2);
}
class IdleAlways implements MessageQueue.IdleHandler{
/**
*返回值爲true,則在messagequeue空閒時繼續執行;否則執行一次後就從messagequeue中remove掉。
*/
@Override
public boolean queueIdle() {
//做些低優先級的邏輯,可執行多次
return true;
}
}
class IdleOnce implements MessageQueue.IdleHandler{
@Override
public boolean queueIdle() {
//只執行一次
return false;
}
}
原理:
android主線程looper是在ActivityThread.java的main函數實例化的, 主線程使用一個MessageQueue實例。 安卓消息就是個死循環, 如果消息過多或者消息執行週期比較長就會造成UI卡頓。
爲了避免UI卡頓, 可以將必須在主線程執行的低優先級任務放到IdleHandler中。
IdleHandler其實就是觀察者模式, 監聽messagequeue是否空閒,空閒時觸發回調函數。
MessageQueue實際上是個單向鏈表, 保存首節點引用。
public final class MessageQueue {
@SuppressWarnings("unused")
private long mPtr; // used by native code
//單向鏈表
Message mMessages;
//支持多個handler, 其實就是觀察者模式。
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
Message next() {
//
for (;;) {
......
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//爲了避免觀察者過多導致耗時, 設置最大數量爲4個。 即空閒時最多觸發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(); //執行所有的idlehandler
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler); //移除當前idlehandler回調
}
}
}
}
}
再看下Looper.java
public final class Looper {
public static void loop() {
...
for (;;) { //死循環獲取下一個message
Message msg = queue.next(); // might block
...
try {
msg.target.dispatchMessage(msg); //target就是Handler實例
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
...
}
}
一張圖說明messagequeue執行邏輯, 當執行sendMessage時添加到mMessages單向鏈表的末尾,當執行sendMessageDelayed時會計算絕對時間並遍歷mMessages找到合適的位置插入。
例如sendMssageDelayed(1, 2000);
sendMessagDelayed(2, 1000);
那麼會按照時間先後順序添加到mMessages裏, Message 2位於Message 1之前。
當mMessageQueue爲空時說明主線程空閒, 這時會執行對應的觀察者IdleHandler, 但MessageQueue設置了閾值4即最多調用4個觀察者的回調函數。
思考: Activity A裏發的延遲消息是否會被Activity B接收到? 取決於Message的target參數指向的是哪個實例。