Handler / MessageQueue / Looper 的個人理解( 源碼分析 )

根據源碼註釋說明, handler設計目的如下:

There are two main uses for a Handler: (1) to schedule messages and
runnables to be executed as some point in the future; and (2) to enqueue
an action to be performed on a different thread than your own.

Handler有兩個主要用途: 
1. 把 Message 和 Runnable 安排在未來的某個時間點執行;
2. 把某些操作放入其他的線程中執行.

注意, Handler 只是發送消息的一個工具, 並不是使用 Thread 所必須的.


具體機制

  1. Handler 初始化: 初始時會獲取當前 ThreadLooper, 其中 android 主線程會自動初始化一個 Looper, 而自己創建的線程則需要自己維護 Looper(通過 Looper.prepare 創建, 通過 Looper.loop 開啓處理 message 的循環 ).

        public Handler(Callback callback, boolean async) {
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue; // 獲取對應線程的Looper的MessageQueue
            mCallback = callback;
            mAsynchronous = async;
        }
        
    
  2. Handler發送消息: Handler 發送消息是把 Message 發送給 Looper 內的 MessageQueue, 所有類型的消息發送到最後都是調用的 enqueueMessage. 而 handler.enqueueMessage 則是調用的 MessageQueue.enqueueMessage

    	// Handler.enqueueMessage
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    // MessageQueue.enqueueMessage 省略了部分代碼
    boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            if (mQuitting) {
                msg.recycle();
                return false;
            }
    
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // 如果當前隊列爲空/已阻塞, 則喚醒
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 沒有阻塞, 把新消息放入消息隊列中(鏈表)
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    
  3. 處理消息: Looper 中會死循環查找 MessageQueue, 如果next Message != null, 那麼這個 message 就會交給它內部的 target Handler 處理 (msg.target.dispatchMessage(msg)) ;

    // 省略了部分代碼, 僅保留邏輯部分
    public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // 可能阻塞
            if (msg == null) {
                return;
            }
            msg.target.dispatchMessage(msg);
            msg.recycleUnchecked(); // 清空
        }
    }
    
    	/**
    	* 由此可知, 如果message中包含runnable, 或者Handler實例化時傳入了callback,
    	* 那麼handler子類重寫的handleMessage就不會執行了. 
    	*/ 
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
    	        // 調用 Message 內部的 Runnable callback
                handleCallback(msg); 
            } else {
                if (mCallback != null) {
    	            // 使用構造方法中傳入的Handler內部的Callback接口
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                // 或者使用子類的處理方式
                handleMessage(msg);
            }
        }
    

新建Thread的兩種方式

第一種:

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i("TAG", "run on Runnable");
            }
        }) {
            @Override
            public void run() {
                super.run();
                Log.i("TAG", "run on Thread");
            }
        }.start();

這種創建方式線程在執行完代碼後( 先執行 Thread.run, Runnable 的 run 執行在 Thread.run 的 super 裏面 )就結束了.

第二種:

        new Thread(new Runnable() {
            @Override
            public void run() {
	            Looper.prepare();
                Log.i("TAG", "run on Runnable");
                Looper.loop();
            }
        }).start();

這種方式開啓了死循環, Thread 會一直存活下去. 可以命名此 Thread, 使用 getLooper().quit 來銷燬.


HandlerThread

這個類繼承自Thread,並在內部start時自動創建出Looper循環,所以可以使用new Handler(HandlerThread.getLooper())的方式很方便的去關聯一個Handler,在循環開啓一個較長時間存活的子線程時可以使用。如果不再需要時,可以通過HandlerThread.quitSafely去退出該線程。


Handler的bug

在使用Handler去循環發送消息時容易出現丟失消息的問題,例如多次在接收到消息時再次延遲發送一個消息時最容易出現,這種場景下就不要使用Handler的方式去處理了,可以直接在Thread.run中添加sleep處理。

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