Android性能優化系列-IdleHandler

性能優化

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參數指向的是哪個實例。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章