—诀艰、Looper , Handler , Message之間的關(guān)系
Looper負(fù)責(zé)的就是創(chuàng)建一個MessageQueue吼旧,然后進(jìn)入一個無限循環(huán)體不斷從MessageQueue中讀取消息毡惜,而消息的創(chuàng)建者就是一個或多個Handler 澎埠。若消息隊(duì)列為空捣炬,線程則會阻塞等待浇揩。
1.Looper
sThreadLocal是一個ThreadLocal對象仪壮,可以在一個線程中存儲變量「旎眨可以看到积锅,在第78行,將一個Looper的實(shí)例放入了ThreadLocal养盗,并且75-77行判斷了sThreadLocal是否為null缚陷,否則拋出異常。這也就說明了Looper.prepare()方法不能被調(diào)用兩次往核,同時也保證了一個線程中只有一個Looper實(shí)例
在構(gòu)造方法中箫爷,創(chuàng)建了一個MessageQueue(消息隊(duì)列)。
方法直接返回了sThreadLocal存儲的Looper實(shí)例聂儒,如果me為null則拋出異常虎锚,也就是說looper方法必須在prepare方法之后運(yùn)行。
第114行:拿到該looper實(shí)例中的mQueue(消息隊(duì)列)
121到153行:就進(jìn)入了我們所說的無限循環(huán)衩婚。
122行:取出一條消息窜护,如果沒有消息則阻塞。
135行:使用調(diào)用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理非春。Msg的target是什么呢柄慰?其實(shí)就是handler對象鳍悠,下面會進(jìn)行分析。
152行:釋放消息占據(jù)的資源坐搔。
Looper主要作用:
1藏研、 與當(dāng)前線程綁定沃斤,保證一個線程只會有一個Looper實(shí)例钠乏,同時一個Looper實(shí)例也只有一個MessageQueue。
2化撕、 loop()方法凳忙,不斷從MessageQueue中去取消息业踏,交給消息的target屬性的dispatchMessage去處理。
好了涧卵,我們的異步消息處理線程已經(jīng)有了消息隊(duì)列(MessageQueue)勤家,也有了在無限循環(huán)體中取出消息的哥們,現(xiàn)在缺的就是發(fā)送消息的對象Handler了柳恐;
2伐脖、Handler
1、首先Looper.prepare()在本線程中保存一個Looper實(shí)例乐设,然后該實(shí)例中保存一個MessageQueue對象讼庇;因?yàn)長ooper.prepare()在一個線程中只能調(diào)用一次,所以MessageQueue在一個線程中只會存在一個近尚。
2蠕啄、Looper.loop()會讓當(dāng)前線程進(jìn)入一個無限循環(huán),不端從MessageQueue的實(shí)例中讀取消息戈锻,然后回調(diào)msg.target.dispatchMessage(msg)方法歼跟。
3、Handler的構(gòu)造方法格遭,會首先得到當(dāng)前線程中保存的Looper實(shí)例嘹承,進(jìn)而與Looper實(shí)例中的MessageQueue想關(guān)聯(lián)。
4如庭、Handler的sendMessage方法,會給msg的target賦值為handler自身撼港,然后加入MessageQueue中坪它。
5、在構(gòu)造Handler實(shí)例時帝牡,我們會重寫handleMessage方法往毡,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法。
3靶溜、HandlerThread
HandlerThread本質(zhì)上是一個線程類开瞭,繼承自Thread類懒震,但是HandlerThread有自己的Looper對象,可以進(jìn)行l(wèi)ooper循環(huán)嗤详,不斷從MessageQueue中取消息个扰。
可以看出,我們將前面的創(chuàng)建的HandlerThread實(shí)例的Looper對象傳遞給了Handler葱色,這使得該Handler擁有了HandlerThread的Looper對象递宅,通過該Handler發(fā)送的消息,都將被發(fā)送到該Looper對象的MessageQueue中苍狰,且回調(diào)方法的執(zhí)行也是執(zhí)行在HandlerThread這個異步線程中办龄。
值得注意的是,如果在handleMessage()執(zhí)行完成后淋昭,如果想要更新UI俐填,可以用UI線程的Handler發(fā)消息給UI線程來更新。
源碼角度解析HandlerThread
HandlerThread在run()方法中翔忽,執(zhí)行了Looper.prepare()方法英融,mLooper保存并獲得了當(dāng)前線程的Looper對象,并通過notifyAll()方法去喚醒等待線程呀打,最后執(zhí)行了Looper.loop()開啟looper循環(huán)語句矢赁。也就是說,run()方法的主要作用就是完成looper機(jī)制的創(chuàng)建贬丛。
Handler通過getLooper()方法獲取looper對象撩银。該方法首先判斷當(dāng)前線程是否啟動?如果沒有啟動,返回null豺憔;如果啟動额获,進(jìn)入同步語句。如果mLooper為null恭应,代表mLooper沒有被賦值抄邀,則當(dāng)前調(diào)用線程進(jìn)入等待階段。直到Looper對象被創(chuàng)建且通過notifyAll()方法喚醒等待線程昼榛,最后才會返回Looper對象境肾。我們看到在run()方法中,mLooper在得到Looper對象后胆屿,會發(fā)送notifyAll()方法來喚醒等待的線程奥喻。
Looper對象的創(chuàng)建在子線程的run()方法中執(zhí)行,但是調(diào)用getLooper()的地方是在主線程中運(yùn)行非迹,我們無法保證在調(diào)用getLooper()時Looper已經(jīng)被成功創(chuàng)建环鲤,所以會在getLooper()中存在一個同步的問題,通過等待喚醒機(jī)制解決了同步的問題
HandlerThread退出
無論是調(diào)用quit()方法還是quitSafely()憎兽,最終都將執(zhí)行到MessageQueue中的quit()方法冷离。
quit()方法最終執(zhí)行的removeAllMessagesLocked()方法吵冒,該方法主要是把MessageQueue消息池中所有的消息全部清空,無論是延遲消息(延遲消息是指通過sendMessageDelayed或通過postDelayed等方法發(fā)送)還是非延遲消息西剥。
quitSafely()方法痹栖,其最終執(zhí)行的是MessageQueue中的removeAllFutureMessagesLocked方法,該方法只會清空MessageQueue消息池中所有的延遲消息蔫耽,并將消息池中所有的非延遲消息派發(fā)出去讓Handler去處理完成后才停止Looper循環(huán)结耀,quitSafely相比于quit方法安全的原因在于清空消息之前會派發(fā)所有的非延遲消息。最后需要注意的是Looper的quit方法是基于API 1匙铡,而Looper的quitSafely方法則是基于API 18的图甜。