一获印、概述
Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關的概念紧唱。那么什么叫異步消息處理線程呢?異步消息處理線程啟動后會進入一個無限的循環(huán)體之中隶校,每循環(huán)一次漏益,從其內(nèi)部的消息隊列中取出一個消息,然后回調(diào)相應的消息處理函數(shù)深胳,執(zhí)行完成一個消息后則繼續(xù)循環(huán)绰疤。若消息隊列為空,線程則會阻塞等待舞终。那么和Handler 轻庆、 Looper 、Message有啥關系敛劝?其實Looper負責的就是創(chuàng)建一個MessageQueue余爆,然后進入一個無限循環(huán)體不斷從該MessageQueue中讀取消息,而消息的創(chuàng)建者就是Handler 夸盟。啥也不說了直接看源碼
二蛾方、源碼分析
1.Looper
對于Looper主要是prepare()和loop()兩個方法。首先看prepare()方法
sThreadLocal是一個ThreadLocal對象上陕,可以在一個線程中存儲變量桩砰。可以看到释簿,在第91行亚隅,將一個Looper的實例放入了ThreadLocal,并且2-在前面判斷sThreadLocal是否為null庶溶,否則拋出異常煮纵。這也就說明了Looper.prepare()方法不能被調(diào)用兩次,同時也保證了一個線程中只有一個Looper實例渐尿。
再來看Looper的構造方法:
在構造方法中醉途,創(chuàng)建了一個MessageQueue(消息隊列)。
然后我們來看loop()方法:
圖3+圖4是整個的looper方法代碼(代碼一屏截不完所以分兩次截茸┤住)
第124行:
public static Looper myLooper() {
return sThreadLocal.get();
}
方法直接返回了sThreadLocal存儲的Looper實例隘擎,如果me為null則拋出異常,也就是說looper方法必須在prepare方法之后運行凉夯。
第128行:拿到該looper實例中的mQueue(消息隊列)。
135到177行:就進入了我們所說的無限循環(huán)铺坞。
136行:取出一條消息慕购,如果沒有消息則阻塞。
154行:使用調(diào)用? msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理休傍。msg的target其實就是handler對象,接下來會進行分析蹲姐。
176行:釋放消息占據(jù)的資源磨取。
Looper主要作用:
1、與當前線程綁定柴墩,保證一個線程只會有一個Looper實例忙厌,同時一個Looper實例也只有一個MessageQueue。
2江咳、loop()方法逢净,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理歼指。
2.Handler
使用Handler之前爹土,我們都是初始化一個實例,比如用于更新UI線程踩身,我們會在聲明的時候直接初始化胀茵,或者在onCreate中初始化Handler實例。所以我們首先看Handler的構造方法惰赋,看其如何與MessageQueue聯(lián)系上的宰掉,它在子線程中發(fā)送的消息(一般發(fā)送消息都在非UI線程)怎么發(fā)送到MessageQueue中的。
198行:通過Looper.myLooper()獲取了當前線程保存的Looper實例赁濒,然后在203行又獲取了這個Looper實例中保存的MessageQueue(消息隊列)轨奄,這樣就保證了handler的實例與我們Looper實例中MessageQueue關聯(lián)上了。
然后看我們最常用的sendMessage方法
sendMessage方法最終調(diào)用了enqueueMessage方法拒炎。
enqueueMessage方法中首先為msg.target賦值為this挪拟,也就印證了上面我們說的msg.target是handler對象。最終會調(diào)用queue的enqueueMessage的方法击你,也就是說handler發(fā)出的消息玉组,最終會保存到消息隊列中去。
現(xiàn)在已經(jīng)很清楚了Looper會調(diào)用prepare()和loop()方法丁侄,在當前執(zhí)行的線程中保存一個Looper實例惯雳,這個實例會保存一個MessageQueue對象,然后當前線程進入一個無限循環(huán)中去鸿摇,不斷從MessageQueue中讀取Handler發(fā)來的消息石景。然后再回調(diào)創(chuàng)建這個消息的handler中的dispathMessage方法,下面我們趕快去看一看這個方法:
到此,整個消息機制分析完畢了潮孽,讓我們首先總結(jié)一下
1揪荣、首先Looper.prepare()在本線程中保存一個Looper實例,然后該實例中保存一個MessageQueue對象往史;因為Looper.prepare()在一個線程中只能調(diào)用一次仗颈,所以MessageQueue在一個線程中只會存在一個。
2椎例、Looper.loop()會讓當前線程進入一個無限循環(huán)挨决,不斷從MessageQueue的實例中讀取消息,然后回調(diào)msg.target.dispatchMessage(msg)方法處理粟矿。
3凰棉、Handler的構造方法损拢,會首先得到當前線程中保存的Looper實例陌粹,進而與Looper實例中的MessageQueue相關聯(lián)。
4福压、Handler的sendMessage方法掏秩,會給msg的target賦值為handler自身,然后加入MessageQueue中荆姆。
5蒙幻、在構造Handler實例時,我們會重寫handleMessage方法胆筒,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法邮破。
三、補充
1.有的人可能會問仆救,在Activity中抒和,我們并沒有顯示的調(diào)用Looper.prepare()和Looper.loop()方法,為啥Handler可以成功創(chuàng)建呢彤蔽,這是因為在Activity的啟動代碼中摧莽,已經(jīng)在當前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法(具體可以查看ActivityThread.java 代碼)。
2.Handler不僅可以更新UI顿痪,你完全可以在一個子線程中去創(chuàng)建一個Handler镊辕,然后使用這個handler實例在任何其他線程中發(fā)送消息,最終處理消息的代碼都會在你創(chuàng)建Handler實例的線程中運行蚁袭。