Handler使用及源碼分析

Pasted Graphic 6.png

Pasted Graphic 7.png

其實(shí)post()也是使用的sendMessage();


image.png

使用方法:
通過Mesage配合sendMessage(Msg)使用

通過Runnable配合post(Runnable)使用

源碼簡析:
handler發(fā)消息機(jī)制用到了looper,messageQueue,handler,hadlerMessage
首先在activity的入口activityThread會(huì)創(chuàng)建一個(gè)輪訓(xùn)器looper,輪訓(xùn)器會(huì)new一個(gè)消息隊(duì)列messageQueue,輪訓(xùn)器一直會(huì)輪訓(xùn)消息隊(duì)列,監(jiān)測(cè)是否有新的消息。然后在創(chuàng)建的handler的中的一個(gè)方法,handler.sendMeassage(),負(fù)責(zé)將handler的消息發(fā)送給消息隊(duì)列,輪訓(xùn)器時(shí)刻監(jiān)測(cè)消息隊(duì)列,當(dāng)檢測(cè)到新的消息,就會(huì)取出新的信息發(fā)送給handlerMeasage()進(jìn)行消息的處理。


handle消息機(jī)制圖

1.首先是Looper和MessageQueue的創(chuàng)建,主線程一創(chuàng)建時(shí),就會(huì)調(diào)用prepareMainLooper()方法,在此方法中創(chuàng)建Looper,然后通過ThreadLocal來保存這個(gè)Looper,ThreadLocal是一個(gè)線程級(jí)的單例,一個(gè)線程里面只能放一個(gè)對(duì)象,所以通過ThreadLoacal保證了線程和Looper的一一對(duì)應(yīng)的關(guān)系,Looper在創(chuàng)建的時(shí)候創(chuàng)建了一個(gè)MessageQueue對(duì)象,通過Loop中的一個(gè)final成員變量保存起來,這樣就保證了一個(gè)線程中只能有一個(gè)MessageQueue,此時(shí)主線程不能再創(chuàng)建looper了,子線程要想使用消息機(jī)制,要調(diào)用Looper.Prepare()方法。


image.png

2.Looper和MessageQueue創(chuàng)建之后,就會(huì)調(diào)用Looper.loop()方法使輪訓(xùn)器轉(zhuǎn)起來,這是一個(gè)阻塞的死循環(huán),不斷地到消息隊(duì)列中去取消息,(阻塞的死循環(huán)可以說一下,安卓程序的入口ActivityThread是main方法,正如java的main方法,只要main方法走完(代碼運(yùn)行完畢),java程序就會(huì)退出,而安卓程序是可以停止在界面上的,這是為什么呢?就是因?yàn)槌绦騼?nèi)部有一個(gè)死循環(huán)一直在跑,那為什么主線程一直阻塞卻沒有崩潰呢?就是因?yàn)橛袀€(gè)消息隊(duì)列,比如你按一下按鈕,就會(huì)把這個(gè)消息扔到消息隊(duì)列中,looper發(fā)現(xiàn)新消息就會(huì)取出,然后開始操作UI了。)。通過msg.target.dispatchMessage()方法取出消息,之后dispatchMessage()方法中又調(diào)用了handleMessage(msg)去取出消息更新到主線程來。
image.png

image.png

image.png

其實(shí)msg.target.dispatchMessage()中的target就是一個(gè)Handler,所以整個(gè)流程其實(shí)就是通過Handler將消息傳遞給了消息隊(duì)列,然后消息隊(duì)列又將消息分發(fā)給了Handler來處理消息。所以Handler有兩種作用:(1)接受消息發(fā)送消息,(2)處理消息。


image.png

3.第三步就是通過handler發(fā)消息了,發(fā)消息通過handler.sendMessage()方法,此方法有一系列的相關(guān)的方法:sendMessageDelay()等,其實(shí)這些方法都相當(dāng)于可以控制時(shí)間的sendMessageAtTime()方法,這個(gè)方法又會(huì)去調(diào)用MessageQueue.enqueueMessage()方法,也就是將消息放到消息隊(duì)列的過程,具體怎么放的?enqueueMesssage拿著message與所有消息進(jìn)行比較,根據(jù)每個(gè)消息要執(zhí)行的時(shí)間將消息放到一個(gè)合適的位置。就是根據(jù)執(zhí)行時(shí)間,先執(zhí)行的方法放到消息隊(duì)列的前面,后執(zhí)行的放到后面,
那消息隊(duì)列是如何保存消息的呢?其實(shí)MessageQueue中只存入了一個(gè)消息mMessage,就是消息隊(duì)列中的第一條消息,每一個(gè)消息都有一個(gè)屬性Message.next,可以指向下一個(gè)消息,
4.還有就是消息的創(chuàng)建了,調(diào)用Message.obtain();有一個(gè)消息池的概念,首先調(diào)用Message.recycle()方法,進(jìn)行回收消息,使msg.what=0; msg.obj=null; 將消息池清空之后,就可以放入第一條消息,然后一路.next將下一個(gè)消息的放入。有一個(gè)消息mPool(也是一個(gè)Message)可以記錄消息池中消息的數(shù)量,消息池中最多只能放50條消息


image.png

大家應(yīng)該都會(huì)聽說過Handler可能導(dǎo)致內(nèi)存泄漏,那么是為什么呢?是因?yàn)镴ava中當(dāng)我們使用普通內(nèi)部類時(shí),它會(huì)持有外部類的引用,哪怕沒有明確的引用其實(shí)也會(huì)隱式的持有外部類的引用,如圖:


很明顯的持有Activity的引用

這時(shí)候我們就可以分析得到,Handler引用著這個(gè)Activity,而Message又引用著Handler(因?yàn)镸essage中的target就是當(dāng)前發(fā)消息的Handler),而MessageQueue又引用著Message且MessageQueue隨著主線程的Looper會(huì)一直存在(因?yàn)楫?dāng)前是在主線程中使用Handler),哪怕這個(gè)Activity馬上調(diào)用finish(),也不并不會(huì)被Java的垃圾回收機(jī)制回收,因?yàn)樗€被別人引用著,這個(gè)時(shí)候我們需要想到如何解決它。
這個(gè)時(shí)候其實(shí)我們可以使用靜態(tài)內(nèi)部類去創(chuàng)建Handler,因?yàn)殪o態(tài)內(nèi)部類并不用持有外部類的引用,所以我們也就不用擔(dān)Activity不會(huì)被回收,但是如果我們還是需要在Handler中使用Activity呢?那么可以使用Java的弱引用,從而使得Activity可以被Java回收掉,使用方法如下:


image.png

還有一點(diǎn)我們可以進(jìn)一步優(yōu)化就是當(dāng)Activity回調(diào)onDestroy時(shí),我們可以Handler的removeMessages/removeCallback取消任務(wù),
所以總結(jié):
image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容