其實(shí)post()也是使用的sendMessage();
使用方法:
源碼簡析:
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)行消息的處理。
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()方法掂碱。
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)去取出消息更新到主線程來岁歉。
其實(shí)msg.target.dispatchMessage()中的target就是一個(gè)Handler,所以整個(gè)流程其實(shí)就是通過Handler將消息傳遞給了消息隊(duì)列膝蜈,然后消息隊(duì)列又將消息分發(fā)給了Handler來處理消息锅移。所以Handler有兩種作用:(1)接受消息發(fā)送消息,(2)處理消息饱搏。
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條消息
大家應(yīng)該都會(huì)聽說過Handler可能導(dǎo)致內(nèi)存泄漏薛匪,那么是為什么呢捐川?是因?yàn)镴ava中當(dāng)我們使用普通內(nèi)部類時(shí),它會(huì)持有外部類的引用逸尖,哪怕沒有明確的引用其實(shí)也會(huì)隱式的持有外部類的引用古沥,如圖:
這時(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回收掉,使用方法如下:
還有一點(diǎn)我們可以進(jìn)一步優(yōu)化就是當(dāng)Activity回調(diào)onDestroy時(shí)陷揪,我們可以Handler的removeMessages/removeCallback取消任務(wù)惋鸥,
所以總結(jié):