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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市悍缠,隨后出現(xiàn)的幾起案子卦绣,更是在濱河造成了極大的恐慌,老刑警劉巖飞蚓,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滤港,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡趴拧,警方通過查閱死者的電腦和手機(jī)溅漾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來著榴,“玉大人添履,你說我怎么就攤上這事∧杂郑” “怎么了暮胧?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長问麸。 經(jīng)常有香客問我往衷,道長,這世上最難降的妖魔是什么严卖? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任炼绘,我火速辦了婚禮,結(jié)果婚禮上妄田,老公的妹妹穿的比我還像新娘俺亮。我一直安慰自己,他們只是感情好疟呐,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布脚曾。 她就那樣靜靜地躺著,像睡著了一般启具。 火紅的嫁衣襯著肌膚如雪本讥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天鲁冯,我揣著相機(jī)與錄音拷沸,去河邊找鬼。 笑死薯演,一個(gè)胖子當(dāng)著我的面吹牛撞芍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播跨扮,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼序无,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了衡创?” 一聲冷哼從身側(cè)響起帝嗡,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎璃氢,沒想到半個(gè)月后哟玷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡一也,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年巢寡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塘秦。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡讼渊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尊剔,到底是詐尸還是另有隱情爪幻,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布须误,位于F島的核電站挨稿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏京痢。R本人自食惡果不足惜奶甘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祭椰。 院中可真熱鬧臭家,春花似錦疲陕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至你踩,卻和暖如春诅岩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背带膜。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工吩谦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膝藕。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓式廷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親束莫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子懒棉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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