天天看點

講講Handler+Looper+MessageQueue 關系

Handler+Looper+MessageQueue這三者的關系其實就是Android的消息機制。這塊内容相比開發人員都不陌生,在面試中,或者日常開發中都會碰到,今天就來講這三者的關系。

Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關的概念。那麼什麼叫異步消息處理線程呢?

異步消息處理線程啟動後會進入一個無限的循環體之中,每循環一次,從其内部的消息隊列中取出一個消息,然後回調相應的消息處理函數,執行完成一個消息後則繼續循環。若消息隊列為空,線程則會阻塞等待。

那麼Android消息機制主要是指Handler的運作機制,Handler運作需要底層的MessageQueue和Looper支撐。其中MessageQueue采用的是單連結清單的結構,Looper可以叫做消息循環。由于MessageQueue隻是一個消息存儲單元,不能去處理消息,而Looper就是專門來處理消息的,Looper會以無限循環的形式去查找是否有新消息,如果有的話,就處理,否則就一直等待着。

我們知道,Handler建立的時候會采用目前線程的Looper來構造消息循環系統,需要注意的是,線程預設是沒有Looper的,如果需要使用Handler就必須為線程建立Looper,因為預設的UI主線程,也就是ActivityThread,ActivityThread被建立的時候就會初始化Looper,這也是在主線程中預設可以使用Handler的原因。

先給出這三者之間的關系圖

講講Handler+Looper+MessageQueue 關系

前面提到的Handler+Looper+MessageQueue,這三者實際上是一個整體,我們在開發過程中接觸的最多是Handler。Handler的主要作用是将一個任務切換到某個指定的線程中去執行,那麼Androd為什麼要提供這個功能呢?

這是因為Android規定UI隻能在主線程中進行,如果在子線程中通路UI,那麼程式就會崩潰,抛出異常,這就是導緻我們不能在主線程中進行耗時操作,否則會導緻程式無法響應,即ANR,那要是我們想要從服務端擷取資料在UI上顯示怎麼辦呢,耗時的話我們一般在子線程中進行擷取,如何把擷取的資料呈現在主線程中呢,這其中就用到了Handler,Handler主要原因就是為了解決在子線程中無法通路UI的沖突。

可以在延伸下,系統為什麼不允許在子線程中通路UI呢,這是因為Android的UI控件不是線程安全的,如果是多線程中并發通路可能會導緻UI控件處于不可控的狀态。

Looper扮演的角色就是消息循環,不斷從MessageQueue中檢視是否有新消息,如果有新消息到來就會立刻處理,否則就一直祖塞在那裡,在它的構造方法,預設會建立一個MessageQueue的消息隊列,然後将目前線程的對象儲存起來。

對于Looper主要是prepare()和loop()兩個方法,首先看prepare()方法。

sThreadLocal是一個ThreadLocal對象,可以在一個線程中存儲變量。可以看到,在第5行,将一個Looper的執行個體放入了ThreadLocal,并且2-4行判斷了sThreadLocal是否為null,否則抛出異常。這也就說明了Looper.prepare()方法不能被調用兩次,同時也保證了一個線程中隻有一個Looper執行個體

然後我們看loop()方法:

方法直接傳回了sThreadLocal存儲的Looper執行個體,如果me為null則抛出異常,也就是說looper方法必須在prepare方法之後運作。

拿到該looper執行個體中的mQueue(消息隊列)

就進入了我們所說的無限循環。

取出一條消息,如果沒有消息則阻塞。

使用調用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。Msg的target是什麼呢?其實就是handler對象,下面會進行分析。

釋放消息占據的資源。

1、 與目前線程綁定,保證一個線程隻會有一個Looper執行個體,同時一個Looper執行個體也隻有一個MessageQueue。

2、 loop()方法,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理。

好了,我們的異步消息處理線程已經有了消息隊列(MessageQueue),也有了在無限循環體中取出消息的哥們,現在缺的就是發送消息的對象了,于是乎:Handler登場了。

Handler的工作主要是包含消息的發送和接受的過程。使用Handler之前,我們都是初始化一個執行個體,比如用于更新UI線程,我們會在聲明的時候直接初始化,或者在onCreate中初始化Handler執行個體。

通過Looper.myLooper()擷取了目前線程儲存的Looper執行個體,然後在19行又擷取了這個Looper執行個體中儲存的MessageQueue(消息隊列),這樣就保證了handler的執行個體與我們Looper執行個體中MessageQueue關聯上了。

然後看我們最常用的sendMessage方法

最後調用了sendMessageAtTime,在此方法内部有直接擷取MessageQueue然後調用了enqueueMessage方法,我們再來看看此方法:

enqueueMessage中首先為meg.target指派為this,如果大家還記得Looper的loop方法會取出每個msg然後交給msg,target.dispatchMessage(msg)去處理消息,也就是把目前的handler作為msg的target屬性。最終會調用queue的enqueueMessage的方法,也就是說handler發出的消息,最終會儲存到消息隊列中去。

現在已經很清楚了Looper會調用prepare()和loop()方法,在目前執行的線程中儲存一個Looper執行個體,這個執行個體會儲存一個MessageQueue對象,然後目前線程進入一個無限循環中去,不斷從MessageQueue中讀取Handler發來的消息。然後再回調建立這個消息的handler中的dispathMessage方法,下面我們趕快去看一看這個方法:

可以看到這是一個空方法,為什麼呢,因為消息的最終回調是由我們控制的,我們在建立handler的時候都是複寫handleMessage方法,然後根據msg.what進行消息處理。

總結下:

1、首先Looper.prepare()在本線程中儲存一個Looper執行個體,然後該執行個體中儲存一個MessageQueue對象;因為Looper.prepare()在一個線程中隻能調用一次,是以MessageQueue在一個線程中隻會存在一個。

2、Looper.loop()會讓目前線程進入一個無限循環,不端從MessageQueue的執行個體中讀取消息,然後回調msg.target.dispatchMessage(msg)方法。

3、Handler的構造方法,會首先得到目前線程中儲存的Looper執行個體,進而與Looper執行個體中的MessageQueue想關聯。

4、Handler的sendMessage方法,會給msg的target指派為handler自身,然後加入MessageQueue中。

5、在構造Handler執行個體時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。

看代碼

可以看到,在getPostMessage中,得到了一個Message對象,然後将我們建立的Runable對象作為callback屬性,指派給了此message.

注:産生一個Message對象,可以new  ,也可以使用Message.obtain()方法;兩者都可以,但是更建議使用obtain方法,因為Message内部維護了一個Message池用于Message的複用,避免使用new 重新配置設定記憶體。最終和handler.sendMessage一樣,調用了sendMessageAtTime,然後調用了enqueueMessage方法,給msg.target指派為handler,最終加入MessagQueue.

可以看到,這裡msg的callback和target都有值,那麼會執行哪個呢?

dispatchMessage方法

講講Handler+Looper+MessageQueue 關系

源于對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中可以看到技術積累的過程。

1,Android系統簡介

2,ProGuard代碼混淆

3,講講Handler+Looper+MessageQueue關系

4,Android圖檔加載庫了解

5,談談Android運作時權限了解

6,EventBus初了解

7,Android 常見工具類

8,對于Fragment的一些了解

9,Android 四大元件之 " Activity "

10,Android 四大元件之" Service "

11,Android 四大元件之“ BroadcastReceiver "

11,Android 四大元件之" ContentProvider "

13,講講 Android 事件攔截機制

14,Android 動畫的了解

15,Android 生命周期和啟動模式

16,Android IPC 機制

17,View 的事件體系

18,View 的工作原理

19,了解 Window 和 WindowManager

20,Activity 啟動過程分析

21,Service 啟動過程分析

22,Android 性能優化

23,Android 消息機制

24,Android Bitmap相關

25,Android 線程和線程池

26,Android 中的 Drawable 和動畫

27,RecylerView 中的裝飾者模式

28,Android 觸摸事件機制

29,Android 事件機制應用

30,Cordova 架構的一些了解

31,有關 Android 插件化思考 

32,開發人員必備技能——單元測試