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。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方法
源于對掌握的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,開發人員必備技能——單元測試