一涎拉、handler的消息機制
一句話:handler通過sendMessage發(fā)送Message到MessageQueue瑞侮,Looper通過loop()不斷地輪詢MessageQueue,調(diào)用Handler的dispatchMessage將Message通知給Callback回調(diào)鼓拧,在回調(diào)中handleMessage()方法接收消息并處理UI更新半火。
二、源碼分析
handler通過sendMessage來發(fā)送Message消息毁枯,通過enqueueMessage方法將Message加入到MessageQueue中,MessageQueue是以鏈表的形式存儲Message的叮称,來看一下Handler的源碼:
在MessageQueue中种玛,
讀源碼可以發(fā)現(xiàn)message是以鏈表的形式串聯(lián)在一起的,將上述代碼形象轉(zhuǎn)化成下圖:
通過上述流程瓤檐,已經(jīng)將新的Message加入到了MessageQueue中赂韵,再來看,這個MessageQueue到底是從哪來的是在哪個線程中的挠蛉?帶著疑問去找Handler
我們回到Handler中祭示,在構(gòu)造方法中找到了MessageQueue的出處,是通過Looper.myLooper()獲取到Looper對象谴古,再從中獲取的消息隊列质涛。那么Looper又是從哪里來的又屬于哪個線程呢?我們繼續(xù)看Looper
通過ThreadLocal(線程局部變量)來get到當前線程的Looper掰担,即與Handler創(chuàng)建的線程一致汇陆,這樣,我們已經(jīng)理清Handler是如何將Message發(fā)送到MessageQueue中的并以鏈表形式存儲的带饱,同時也知道了輪詢消息隊列的Looper是與Handler處于同一線程毡代,這也是處理Handler發(fā)送消息的Looper唯一性的原因阅羹。接下來,我們來看一下Looper又是如何處理消息隊列中的消息的教寂。
查看Looper的源碼捏鱼,其中l(wèi)oop()是啟動輪詢的方法:
首先拿到同線程的Looper并獲取到其中的消息隊列,使用死循環(huán)來遍歷MessageQueue中的Message酪耕,其中msg.target獲取到與message關(guān)聯(lián)的Handler导梆,調(diào)用Handler的dispatchMessage方法,再回到Handler中:
可以知道因妇,我們在創(chuàng)建Handler時所實現(xiàn)的Callback回調(diào)在此時被調(diào)用到问潭,即Looper輪詢?nèi)蝿?wù)后,通過Handler的dispatchMessage方法通知Callback執(zhí)行handleMessage方法婚被,這樣我們在handleMessage中接收消息來更新UI狡忙。
以上完成了Handler的整個消息傳遞機制,我們再來總結(jié)下:
1址芯、在主線程中創(chuàng)建Handler灾茁,在Handler的構(gòu)造方法中獲取到當前線程的Looper和MessageQueue。
2谷炸、當調(diào)用handler.sendMessage(Message msg)方法時北专,通過Handler的enqueue方法將Message發(fā)送到MessageQueue中,MessageQueue中的消息是以鏈表的形式連在一起的旬陡。
3拓颓、Looper通過loop()方法輪詢MessageQueue中的消息,通過調(diào)用Handler的dispatchMessage()方法來通知Callback回調(diào)描孟。
4驶睦、在Handler的Callback回調(diào)中實現(xiàn)handleMessage(Message msg)方法,接收message匿醒,進而更新主線程UI场航。
關(guān)鍵點:
1、Handler廉羔、Looper溉痢、MessageQueue處于同一個線程,為主線程(HandlerThread的Looper是在子線程憋他,使用HandlerThread用來分擔主線程中Looper的壓力孩饼,同時提高優(yōu)先級)。
2竹挡、MessageQueue中的Message以鏈表的形式連在一起捣辆,先進先出。
3此迅、Handler中通過ThreadLocal來獲取當前線程的Looper汽畴,從而保證了Looper的唯一性旧巾,一個Handler發(fā)送的消息不會有多個Looper來進行處理。
4忍些、Message中msg.target獲取到Message綁定的Handler鲁猩,也就是Message持有著Handler的引用。
三罢坝、Hanlder內(nèi)存泄漏的問題及解決
導(dǎo)致內(nèi)存泄漏的原因是非靜態(tài)內(nèi)部類持有外部類引用廓握,當外部類Activity銷毀時,該引用沒有得到及時釋放嘁酿,導(dǎo)致該Activity無法回收隙券,從而造成內(nèi)存泄漏。
解決方案:
1闹司、將Handler改為靜態(tài)內(nèi)部類娱仔,用static修飾
2、內(nèi)部使用Activity的弱引用
3游桩、在Activity的onDestroy()方法中removeCallback
不多說牲迫,直接上代碼:
原創(chuàng)文章,轉(zhuǎn)載請注明出處借卧,謝謝盹憎!