Handler的原理
我的簡書: ofollow,noindex">https://www.jianshu.com/u/c91e642c4d90
我的CSDN: http://blog.csdn.net/wo_ha
我的GitHub: https://github.com/chuanqiLjp
我的個人部落格: https://chuanqiljp.github.io/
版權宣告:商業轉載請聯絡我獲得授權,非商業轉載請在醒目位置註明出處。
序言:為了印象更深刻,我寫了一個專案參考了Handler的原始碼進行編寫,是原始碼的一個簡單版本,但是實現的主要功能是有清晰說明的。地址: https://github.com/chuanqiLjp/TestHandler ,歡迎start。。。
Handler的總結歸納
-
MessageQueue:儲存訊息,包含插入和讀取訊息的操作:
-
enqueueMessage():新增訊息,實際上是單鏈表的操作,若訊息佇列滿則新增訊息的執行緒阻塞等待被喚醒;
-
next():讀取訊息伴隨著訊息的刪除(相當於出佇列),是一個無限迴圈,若無訊息則一直阻塞等待,若有新訊息到來則返回該訊息並從連結串列中移除;
-
-
Looper: 一個執行緒只能建立一個Looper物件,一個Looper物件只有一個MessageQueue(在Looper的構造方法中例項化),建立Looper物件使用Looper.prepare(),使用Looper.loop()啟動訊息迴圈,不斷的從MessageQueue中獲取訊息,在交給Message的target屬性所對應的Handler去處理(由於通常Looper物件會在主執行緒中建立並呼叫Looper.loop()去輪詢訊息,因此該方法執行在主執行緒中),若取到空訊息則loop()退出迴圈(呼叫了Looper的quit()或quitSafely()才會取到空訊息);
-
Handler:訊息的傳送和處理,
-
傳送訊息:通過post()或sendMessage(),再呼叫MessageQueue的enqueueMessage()插入訊息佇列
-
處理訊息:當Looper的loop()方法中的迴圈呼叫MessageQueue的next()取到訊息後,呼叫 msg.target.dispatchMessage(msg)進行分發,其Handler中事件處理的優先順序順序:Message.callback(Runnable) -> mCallBack(CallBack介面的子類) ->Handler或子類的handleMessage()【平時使用的是優先順序最低的】
-
單執行緒模型中Message、Handler、MessageQueue、Looper之間的關係
簡單的說,Handler獲取當前執行緒中的looper物件,looper用來從存放Message的MessageQueue中取出Message,再有Handler進行Message的分發和處理.
Message Queue(訊息佇列):用來存放通過Handler釋出的訊息,通常附屬於某一個建立它的執行緒,可以通過Looper.myQueue()得到當前執行緒的訊息佇列
Handler:可以釋出或者處理一個訊息或者操作一個Runnable,通過Handler釋出訊息,訊息將只會傳送到與它關聯的訊息佇列,然也只能處理該訊息佇列中的訊息
Looper:是Handler和訊息佇列之間通訊橋樑,程式元件首先通過Handler把訊息傳遞給Looper,Looper把訊息放入佇列。Looper也把訊息佇列裡的訊息廣播給所有的
Handler:Handler接受到訊息後呼叫handleMessage進行處理
Message:訊息的型別,在Handler類中的handleMessage方法中得到單個的訊息進行處理
在單執行緒模型下,為了執行緒通訊問題,Android設計了一個Message Queue(訊息佇列), 執行緒間可以通過該Message Queue並結合Handler和Looper元件進行資訊交換。下面將對它們進行分別介紹:
-
Message
Message訊息,理解為執行緒間交流的資訊,處理資料後臺執行緒需要更新UI,則傳送Message內含一些資料給UI執行緒。
-
Handler
Handler處理者,是Message的主要處理者,負責Message的傳送,Message內容的執行處理。後臺執行緒就是通過傳進來的 Handler物件引用來sendMessage(Message)。而使用Handler,需要implement 該類的 handleMessage(Message)方法,它是處理這些Message的操作內容,例如Update UI。通常需要子類化Handler來實現handleMessage方法。
-
Message Queue
Message Queue訊息佇列,用來存放通過Handler釋出的訊息,按照先進先出執行。
每個message queue都會有一個對應的Handler。Handler會向message queue通過兩種方法傳送訊息:sendMessage或post。這兩種訊息都會插在message queue隊尾並按先進先出執行。但通過這兩種方法傳送的訊息執行的方式略有不同:通過sendMessage傳送的是一個message物件,會被 Handler的handleMessage()函式處理;而通過post方法傳送的是一個runnable物件,則會自己執行。
-
Looper
Looper是每條執行緒裡的Message Queue的管家。Android沒有Global的Message Queue,而Android會自動替主執行緒(UI執行緒)建立Message Queue,但在子執行緒裡並沒有建立Message Queue。所以呼叫Looper.getMainLooper()得到的主執行緒的Looper不為NULL,但呼叫Looper.myLooper() 得到當前執行緒的Looper就有可能為NULL。對於子執行緒使用Looper,API Doc提供了正確的使用方法:這個Message機制的大概流程:
-
在Looper.loop()方法執行開始後,迴圈地按照接收順序取出Message Queue裡面的非NULL的Message。
-
一開始Message Queue裡面的Message都是NULL的。當Handler.sendMessage(Message)到Message Queue,該函式裡面設定了那個Message物件的target屬性是當前的Handler物件。隨後Looper取出了那個Message,則呼叫 該Message的target指向的Hander的dispatchMessage函式對Message進行處理。在dispatchMessage方法裡,如何處理Message則由使用者指定,三個判斷,優先順序從高到低:
-
Message裡面的Callback,一個實現了Runnable介面的物件,其中run函式做處理工作;
-
Handler裡面的mCallback指向的一個實現了Callback介面的物件,由其handleMessage進行處理;
-
處理訊息Handler物件對應的類繼承並實現了其中handleMessage函式,通過這個實現的handleMessage函式處理訊息。
-
由此可見,我們實現的handleMessage方法是優先順序最低的!
- Handler處理完該Message (update UI) 後,Looper則設定該Message為NULL,以便回收!
在網上有很多文章講述主執行緒和其他子執行緒如何互動,傳送資訊,最終誰來執行處理資訊之類的,個人理解是最簡單的方法——判斷Handler物件裡面的Looper物件是屬於哪條執行緒的,則由該執行緒來執行!
-
當Handler物件的建構函式的引數為空,則為當前所線上程的Looper;
-
Looper.getMainLooper()得到的是主執行緒的Looper物件,Looper.myLooper()得到的是當前執行緒的Looper物件。
-
Handler的原理
ThreadLocal
ThreadLocal是一個 執行緒內部的資料儲存類 ,實質上是一個泛型類,定義為:public class ThreadLocal<T>。通過它可以在某個指定執行緒中儲存資料,資料儲存以後,只有在 指定執行緒(儲存資料的執行緒) 中可以獲取到它儲存的資料,對於其他的執行緒來說無法獲取到它的資料。
通過使用ThreadLocal,能夠讓同一個資料物件在不同的執行緒中存在多個副本,而這些副本互不影響。Looper的實現中便使用到了ThreadLocal。通過使用ThreadLocal,每個執行緒都有自己的Looper,它們是同一個資料物件的不同副本,並且不會相互影響。
ThreadLocal中有一個內部類ThreadLocalMap,ThreadLocal中有一個內部類Entry,Entry中的 Object value
這個value實際上就是每一個執行緒中的資料副本。ThreadLocalMap中有一個存放Entry的陣列: Entry[] table
。 ThreadLocal類的部分程式碼如下:

image.png
ThreadLocal的 set
方法:實際上就是往ThreadLocalMap物件(map)維護的物件陣列table中插入資料。

image.png
ThreadLocal的 get
方法,呼叫了ThreadLocalMap的getEntry()方法:

image.png
ThreadLocalMap的 getEntry()
方法:

image.png
i的值是由執行緒的雜湊碼和(table的長度-1)進行“按位與”運算,所有每個執行緒得到的i是不一樣的,因此最終資料副本在table中的位置也不一樣。
MessageQueue
MessageQueue主要包含兩個操作,插入和讀取。讀取操作的函式是 next()
,該操作同時也會伴隨著刪除操作(相當於出佇列),插入操作對應的函式是 enqueueMessage()
, enqueueMessage()
實際上就是單鏈表的插入操作。 next()
方法是一個無限迴圈的方法,如果訊息佇列中沒有訊息,那麼next()方法會一直阻塞。當有新訊息到來時,next()方法會返回這條訊息並將其從單鏈表中移除。
Looper
Looper在Android的訊息機制中扮演著訊息迴圈的角色,它會不停地從MessageQueue中檢視是否有新的Message到來,如果有新訊息就會立刻處理,否則就一直阻塞在那裡。一個執行緒只能有一個Looper物件,從而也只有一個MessageQueue(在Looper的構造方法初始化)。
Looper中的幾個重要的成員變數:

image.png
Looper的構造方法,在構造方法中,建立了一個 MessageQueue 例項:

image.png
當需要為一個執行緒建立Looper物件時,需要呼叫Looper的 prepare()
方法(該方法在一個執行緒中只能呼叫一次,否則會丟擲異常):

image.png
在 loop()
的訊息迴圈中,實際上是呼叫了MessageQueue的 next() 方法。

image.png

image.png
Looper主要作用:
1、 與當前執行緒繫結,保證一個執行緒只會有一個Looper例項,同時一個Looper例項也只有一個MessageQueue。
2、 loop()方法,不斷從MessageQueue中去取訊息,交給訊息的target屬性的dispatchMessage去處理。
Handler
Handler的工作主要是訊息的傳送和訊息接收處理。訊息的傳送可以通過Handler的 post()
方法或者 sendMessage()
方法來實現,訊息的處理,需要我們重寫handleMessage()函式來進行處理。
Handler的sendMessage()函式:

image.png

image.png

image.png
最後呼叫了MessageQueue的 enqueueMessage()
函式:

image.png

image.png
Message 的callback成員變數實際上是一個 Runnable物件 :
Runnable callback;
經常使用的Handler的 post(Runnable r)
方法,原始碼是這樣的:
public final boolean post(Runnable r) { returnsendMessageDelayed(getPostMessage(r), 0); }
其中, getPostMessage(r)
為:
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
原來,Handler的 post()
方法實際上是把這個Runnable物件封裝到了一個Message中的。
因此,Handler中的事件處理優先順序順序是:
Message.callback(Runnable) -- > mCallback(Callback介面實現類或Callback匿名內部類) ---> Handler或其子類的handleMessage()。