1. 程式人生 > >【原始碼解讀】Handler訊息機制流程分析

【原始碼解讀】Handler訊息機制流程分析

之前看《藝術探索》大致瞭解了Android訊息機制的原理,也總結了筆記,但總感覺理解的不夠徹底,之後把原始碼和之前的筆記又看了一遍,算是基本通了,將基本邏輯記錄在此,重在分析流程,不分析具體細節原理。

這裡以主執行緒為例,Handler訊息機制的完整過程如下:

1.執行緒建立Looper, 同時初始化MessageQuene和Thread物件,開啟迴圈

在ActivityThread的main方法中,會首先建立主執行緒的Handler,因此我們在主執行緒中是可以建立Handler物件而不需要考慮Looper問題的。

 public static void main(String[] args) {
    //程式碼省略...
Looper.prepareMainLooper(); Looper.loop(); }

最終通過prepare方法建立Looper物件並使用ThreadLocal進行儲存,原始碼如下:


public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared."
); } sMainLooper = myLooper(); } } //最終呼叫該方法建立Looer物件,因為是主執行緒所以這裡的引數為false private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }

在Looper的建構函式中會同時建立其MessageQuene和Thread物件.這樣就建立好了我們需要的Looper物件以及MessageQueue和Thread物件,並通過Looper.loop()方法開啟無限迴圈

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

2.建立Handler物件

這裡最常使用的方式就是新建一個Handler物件並傳遞一個CallBack物件,當然還有其他的構造方法,這裡不作贅述

    private  Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    String obj = (String) msg.obj;
                    Log.i(TAG, obj);
                    break;
            }
            return true;
        }
    });

3.sendMessage或者post一個Runnable物件

使用建立好的handler物件傳遞訊息,無論是sendMessage還是post一個Runnable物件,最終會呼叫到
sendMessageAtTime,之後會呼叫MessageQuene物件的enqueueMessage()方法將訊息加入到佇列中

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
 }

    //設定msg.target為當前Handler物件,並將訊息儲存進訊息佇列
  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

4.在loop方法中,呼叫handler的dispatcher進行分發.

在prepare好Looper之後,便呼叫了loop方法,其原始碼如下

  public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            /**
            * 呼叫MessageQuene的next方法獲取訊息物件,如果沒有訊息則終止迴圈
            */
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            /**
            * msg.target就是在enqueueMessage方法中設定好的Handler物件,
            * 呼叫dispatchMessage進行訊息分發
            */
            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

loop內部也是無限迴圈,去呼叫MessageQuene的next方法,如果有訊息則拿到訊息物件並通過Handler物件進行分發進行分發

5.dispatchMessage對訊息進行分發,根據傳遞的訊息選擇不同的呼叫,具體如下

   public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
        //檢查Message的callback是否為空,不為空則呼叫handleCallback
        //當我們使用post提交一個Runnable物件的時候回撥用該方法,最終執行我們
        //的Runnable物件的run方法中的內容
            handleCallback(msg);

        } else {
            if (mCallback != null) {
                //當使用sendMessage傳送資訊並建立了CallBack物件時呼叫
                //這就是我們最常見的使用方法,最終會呼叫到我們重寫的handleMessaeg方法
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //呼叫Handler自己的handleMessage,方面裡面為空,啥都不幹
            handleMessage(msg);
        }
    }

簡單來說,在子執行緒通過主執行緒的Handler物件傳送資料時,最終會呼叫到Handler的handleMessage方法進行處理,因為Handler處於主執行緒,那麼此時操作就從子執行緒切換到了主執行緒,從而執行緒間的通訊。

OK,以上就是整個Handler訊息機制的建立處理流程。