Android訊息傳遞機制---Handler,MessageQueue,Looper.
阿新 • • 發佈:2019-02-18
1.Android訊息機制概述以及背景
(1)Looper、Handler、Messagequeue三者共同實現了android系統裡執行緒間通訊機制。
如在A、B兩個子執行緒之間需要傳遞訊息,首先給每個子執行緒繫結一套handler、looper、Messagequeue機制,然後這三個物件都與其所屬執行緒對應。然後A執行緒通過呼叫B執行緒的Handler物件,傳送訊息。這個訊息會被Handler傳送到B執行緒的Messagequeue中,而屬於B執行緒的Looper物件一直在for迴圈裡無限遍歷MessageQueue,一旦發現該訊息佇列裡收到了新的訊息,就會去對訊息進行處理,處理過程中會回撥自身Handler的heandleMessage方法,從而實現了不同執行緒間通訊。
(2)Android規定UI操作只能在主執行緒中進行,當在子執行緒中更新UI時,ViewRootImpl的checkThread方法就會丟擲異常。不允許子執行緒訪問UI是因為Android的UI控制元件不是執行緒安全的,如果在多執行緒中併發訪問會使UI控制元件處於不可預期的狀態。既然如此可能有讀者會問那給UI控制元件的訪問加上鎖不就行了嗎。事實是一:加鎖會使UI訪問的邏輯變得複雜,二是會降低UI訪問的效率,鎖機制會阻塞某些執行緒的執行。綜上使用單執行緒來對UI控制元件進行訪問無疑是比較好的。而Handler的主要作用是將一個任務切換到某個指定的執行緒中去執行,用在這裡再合適不過了。
2. Android訊息機制分析
(1)ThreadLocal工作原理
1.ThreadLocal是一個執行緒內部的資料儲存類,通過它可以在指定執行緒中儲存資料,同時也只能在指定執行緒中獲取儲存到的資料。一般來講,當某些資料是以執行緒為作用域並且不同執行緒具有不同的資料副本的時候,就可以考慮使用ThreadLocal。
2.ThreadLocal的set方法和get方法都是從呼叫該方法的執行緒裡取出一個數組,然後再從陣列中根據當前ThreadLocal的索引去查找出對應的value值,不同執行緒中的陣列是不同的,這就是為什麼通過ThreadLocal可以在不同執行緒中維護一套資料的副本並且彼此互不干擾。瞭解了ThreadLocal的原理後,在回頭重新看看上面的概述中AB執行緒的通訊,可知道Looper的底層工作原理正是基於ThreadLocal實現的。 (2)MessageQueue的工作原理 1.MessageQueue其實是通過單鏈表來維護訊息列表的,但是是以佇列的形式對外提供插入和刪除訊息操作,它只是一個訊息佇列。它包含兩個主要操作enqueueMessage和next,前者是插入訊息,後者是取出一條訊息並移除。
2.next方法是一個無限迴圈的方法,如果訊息佇列中沒有訊息,那麼next方法會一直阻塞在這裡。當有新訊息到來時,next方法會返回這條訊息並將它從連結串列中移除。
(3)Looper的工作原理 1.Looper通過prepare方法來建立,通過loop方法來開啟迴圈,它以無限迴圈的形式去查詢是否有新訊息,如果有的話就去處理訊息,否則就一直等待著。prepareMainLooper方法主要是給主執行緒也就是ActivityThread建立Looper使用的,本質也是呼叫了prepare方法。 2..Looper的loop方法會呼叫MessageQueue的next方法來獲取新訊息,而next是一個阻塞操作,當沒有訊息時,next方法會一直阻塞著在那裡,這也導致了loop方法一直阻塞在那裡。如果MessageQueue的next方法返回了新訊息,Looper就會處理這條訊息:msg.target.dispatchMessage(msg),其中的msg.target就是傳送這條訊息的Handler物件。 3.Looper通過quit和quitSafely方法來終止,他們的區別是:前者會直接退出Looper,後者只是設定一個退出標記,然後把訊息佇列中的已有訊息處理完畢後才安全地退出。Looper退出之後,通過Handler傳送的訊息就會失敗,這個時候Handler的send方法會返回false。如果沒有終止Looper,子執行緒就會一直處於等待的狀態,而如果退出Looper以後,這個執行緒就會立刻終止,因此建議不需要的時候終止Looper。 (4)Handler的工作原理 1.Handler就是用來處理訊息的傳送和接收之後的處理。它依賴於當前執行緒的Looper來構建內部的訊息迴圈系統,如果當前執行緒中不存在Looper的話就會報錯。Handler可以用post方法將一個Runnable投遞到訊息佇列中,也可以用send方法傳送一個訊息投遞到訊息佇列中,其實post最終還是呼叫了send方法。 它的原始碼如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);//當message是runnable的情況,也就是Handler的post方法傳遞的引數,這種情況下直接執行runnable的run方法
} else {
if (mCallback != null) {//如果建立Handler的時候是給Handler設定了Callback介面的實現,那麼此時呼叫該實現的handleMessage方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//如果是派生Handler的子類,就要重寫handleMessage方法,那麼此時就是呼叫子類實現的handleMessage方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
disoatchMessage方法正是我們在Looper的工作原理分析裡面提到的msg.target.dispatchMessage(msg),可以看到該方法對不同的情況作了判斷並做出相應的操作,具體可見程式碼後的註釋。
好了,Android的訊息機制大致就是上面這樣了。