Handler訊息處理機制原始碼解析 上

BaiHongHua發表於2018-04-05

Handler訊息處理機制原始碼解析 上

前言

在 Android 程式設計當中,一般是不可以在子執行緒中更新主執行緒的 UI ,這時候 Android 給我們提供了一套在子執行緒中更新 UI 的訊息機制,即 Handler 訊息處理機制。

Handler 允許你傳送程式訊息和可執行的物件到關聯的執行緒訊息佇列中。每個 Handler 的例項關聯一個執行緒和這個執行緒的訊息佇列。當建立一個 Handler 的時候,系統會自動把這個 Handler 例項繫結到當前建立這個 Handler 的執行緒中。

Handler的基本使用(示例)

[1.0] 在主執行緒中建立一個 Handler 的例項,並重寫 Handler 的 handleMessage(Message msg) 方法

final Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        textView.setText((String)msg.obj);
    }
};

[2.0] 在主執行緒中建立一個子執行緒,並通過 Handler 的例項呼叫 sendMessage 給主執行緒傳送訊息

new Thread(new Runnable() {
    @Override
    public void run() {
        Message message = new Message();
        message.obj = "aaa";
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        handler.sendMessage(message);
    }
}).start();
複製程式碼

上面程式碼功能是實現一個啟動應用程式 3 秒鐘或更新 TextView 控制元件的內容。下面進行 Handler 的原始碼分析,Handler 是如何實現上面的邏輯的:也就是 Handler 通過 sendMessage(message) 把一個 Message 例項如何傳送給對應的 Handler 的 handleMessage(Message msg) 的:

示例原始碼分析

下面先從 Message 的入口,也就是 sendMessage(Message msg) 方法開始分析。通過檢視 sedMessage(Message msg) 的方法原始碼,可知道最後會呼叫到 sendMessageAtTime(Message msg, long uptimeMillis) 方法:

[原始碼 1]
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue; // 1
    ...
    return enqueueMessage(queue, msg, uptimeMillis);
}
複製程式碼

上面的 [原始碼 1],最重要的需要明白註釋 1 處的 mQueue 是來自哪裡的訊息佇列 MessageQueue ,也就是在 Handler 的類中,它是如何初始化拿到例項的。因為在上面的示例原始碼中的 Handler 例項重寫的 handleMessage(Message msg) 是被 dispatchMessage(Message msg) 中呼叫的,如下面的 [原始碼 2] 的註釋 1 處:

[原始碼 2]
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);  // 1
    }
}
複製程式碼

上面的 dispatchMessage(Message msg) 方法的主要的作用是處理系統的訊息的,也可以看作分發系統的訊息。好的,我們的重點不在這裡,那麼是誰呼叫了 dispatchMessage(Message msg) 這個方法呢?通過檢視原始碼我們會發現:

[原始碼 3]
public static void loop() {
    final Looper me = myLooper(); // 2
    ...
    final MessageQueue queue = me.mQueue;
    ...
    for (;;) {  // 3
        Message msg = queue.next(); 
        ...
        Printer logging = me.mLogging;
        ...
        msg.target.dispatchMessage(msg);  // 1
        ...

        // 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();
    }
}
複製程式碼

在上面的 [原始碼 3] 中的註釋 1 處呼叫了 dispatchMessage(Message msg) 方法。在註釋 2 處,通過呼叫 myLooper() 獲取當前執行緒的 Looper 物件,而該 Looper 物件攜帶著當前執行緒的訊息佇列(注意:是當前建立 Handler 的執行緒),而在註釋 3 處,for死迴圈主要的作用是把 queue 這個訊息佇列裡面的 Message 訊息取出,然後通過 msg.target.dispatchMessage(msg) 把訊息分發出去,也就是呼叫了 [原始碼 2] 的 dispatchMessage(msg) 方法。閱讀到這裡的你,可能心裡面有個咕嘟:為什麼通過上面 [原始碼 3] 的註釋 2 處呼叫的 myLooper() 方法就可以獲取到當前執行緒的訊息佇列呢? 好的,在 Android 系統原始碼裡面最精彩的部分:在 Handler 的建構函式裡面:

[原始碼 4]
public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper(); // 1
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // 3 當前執行緒的 Looper 物件所持有的訊息佇列 (mLooper.mQueue 為 final 型別)
    mCallback = callback;
    mAsynchronous = async;
}
複製程式碼

在上面的 [原始碼 4]中的註釋 1 處,可以看到,也是同樣地獲取當前執行緒的 Looper 例項 mLooper,然後在註釋 3 處,把 final 型別的訊息佇列例項賦給 mQueue 這個全域性變數。到了這裡,在 Handler 的建構函式裡面初始化了訊息佇列 MessageQueue,因此,在 [原始碼 1] 中的 mQueue 便是當前執行緒 Looper 物件所持有的訊息佇列:

[原始碼 1]
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue; // 1
    ...
    //queue 訊息佇列
    //msg 需要傳遞傳送的 message
    //uptimeMillis 當前訊息傳送的時間
    return enqueueMessage(queue, msg, uptimeMillis);
}

...

[原始碼 5]
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this; //把當前的 Handler 的例項賦給 target(private Handler target)
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis); // 把訊息送進訊息佇列
}
複製程式碼

到了這裡,[原始碼 1] 裡面呼叫的 enqueueMessage(queue, msg, uptimeMillis) 方法,其邏輯如上面的 [原始碼 5],把需要傳送的 Message 傳送給了 Looper.myLooper().mQueue 這個訊息佇列(MessageQueue 是利用類似於單連結串列的方式, 以 Message 為節點來儲存 msg 的,即以連結串列的方式構造佇列);

閱讀到這裡,你可能會比較疑惑,為什麼可以直接通過 Looper.myLooper() 方法獲取到 Looper 的例項呢? 其實,Android 應用程式的 Activity 例項是由 ActivityThread 建立的,同時,ActivityThread 也會預設去建立主執行緒,也會建立主執行緒的 Looper 的例項。

小結

Handler訊息處理機制原始碼解析 上

對上面原始碼的總結,主要就是: Handler 負責把子執行緒中傳進來的訊息(Message),傳遞給 Looper,由 Looper 把訊息封裝成訊息佇列,然後再有 Handler 從訊息佇列中把訊息分發到子執行緒外面。

哈哈,這次的 Handler 事件處理機制的原理解析就到這裡結束了,感謝你的閱讀...

原文章地址

相關文章