帶著這篇去通關(guān)所有Handler的提問(wèn)(三)

帶著這篇去通關(guān)所有Handler的提問(wèn)(三)

開心一刻

寫在前面:

大家久等了尔当,前陣花了一周的時(shí)間去畢業(yè)旅行屋休,所以更新就拖延了一陣霞怀,話不多說(shuō)欢嘿,我們來(lái)回顧一下本系列的前兩篇文章的思路知識(shí)點(diǎn)

Android消息機(jī)制字典型探究(一)

在第一篇文章中晃跺,我們總結(jié)了Android系統(tǒng)不允許在子線程更新UI的原因揩局,本質(zhì)上是線程安全問(wèn)題,從而引出了Handler掀虎。

Android消息機(jī)制字典型探究(二)

在第二篇文章中凌盯,我們又分析了三種在子線程更新UI的方法,分別是:View.post(param); Activity.runOnUIThread(param); Handler烹玉,當(dāng)我們對(duì)這三種方法的源碼進(jìn)一步分析發(fā)現(xiàn)驰怎,其實(shí)都是對(duì)Handler做了一些封裝,所以本文我們就來(lái)正式全面探究有關(guān)Handler的知識(shí)點(diǎn)二打。

當(dāng)時(shí)我去面試的四家公司县忌,都問(wèn)到了Handler的相關(guān)知識(shí),有深有淺,所以重要程度不言而喻症杏。面試官拿起你的簡(jiǎn)歷装获,讓你談?wù)凥andler,你僅僅在表象上回答了Android線程通信的機(jī)理厉颤,然后面試官緊接著問(wèn)了你如下的幾個(gè)問(wèn)題:

  • Handler是屬于哪個(gè)類的穴豫?

  • Handler、Looper逼友、MessageQueue何時(shí)建立的相互關(guān)系绩郎?

  • 主線程的Looper和MessageQueue是何時(shí)創(chuàng)建的?

  • 在同一線程中翁逞,Looper和MessageQueue是怎樣的數(shù)量對(duì)應(yīng)關(guān)系,與Handler又是怎樣的數(shù)量對(duì)應(yīng)關(guān)系溉仑?

  • MessageQueue中消息為空挖函,線程阻塞掛起等待,為什么不會(huì)造成ANR浊竟?

  • 有關(guān)Handler的內(nèi)存泄漏是怎么一回事怨喘?

一臉萌比

so...光知道表象很可能是不夠的,而且還給自己挖了一個(gè)坑振定,所以我們對(duì)于一個(gè)知識(shí)點(diǎn)的探尋要全面充分一點(diǎn)必怜。下面正式開始本文。

Windows和Android消息機(jī)制的區(qū)別

現(xiàn)在的操作系統(tǒng)普遍采用消息驅(qū)動(dòng)模式后频。Windows操作系統(tǒng)就是典型的消息驅(qū)動(dòng)模型梳庆。但是,Android的消息處理機(jī)制和Windows的消息處理機(jī)制又不太相同卑惜。我給大家畫了圖膏执,看看二者的區(qū)別。

Windows進(jìn)程消息模型
Android進(jìn)程消息模型

通過(guò)消息機(jī)制圖的對(duì)比露久,Windows消息處理模型中更米,存在一個(gè)系統(tǒng)的消息隊(duì)列,這個(gè)隊(duì)列是整個(gè)進(jìn)程的核心毫痕,幾乎所有的動(dòng)作都要轉(zhuǎn)換成消息征峦,然后放到這個(gè)隊(duì)列中,由主線程統(tǒng)一處理消请。

而Android沒(méi)有全局的消息隊(duì)列栏笆,消息隊(duì)列是和某個(gè)線程相關(guān)聯(lián)在一起的。每個(gè)線程最多有一個(gè)消息隊(duì)列臊泰,消息的取出和處理竖伯,也在這個(gè)線程本身中完成。

也就是說(shuō),Android中七婴,如果你想在當(dāng)前線程使用消息模型祟偷,則必須構(gòu)建一個(gè)消息隊(duì)列,而消息機(jī)制的相關(guān)主要類是:Looper打厘、Handler修肠、MessageQueue、Message户盯。

我們并不著急去翻看這些類的源碼嵌施,理清楚底層實(shí)現(xiàn)的邏輯,而且先在宏觀表象上看看莽鸭,Android消息機(jī)制是如何運(yùn)行的吗伤?

Android消息機(jī)制的宏觀原理

先來(lái)看一張Android消息處理類之間的關(guān)系圖

Android消息處理機(jī)制

我們從表象上解釋一下原理,Handler負(fù)責(zé)將Message發(fā)送至當(dāng)前線程的MessageQueue中硫眨,Looper時(shí)時(shí)刻刻監(jiān)視著MessageQueue足淆,將符合時(shí)間要求的Message取出,再帶給發(fā)送消息的那個(gè)Handler通過(guò)HandleMessage處理礁阁。

對(duì)于消息機(jī)制的理解不能僅僅停留在這一步巧号,下面我們從源碼的角度分析一下具體的邏輯細(xì)節(jié)。

Android消息機(jī)制相關(guān)類的源碼分析

其實(shí)寫這篇文章之前姥闭,我就一直在思考丹鸿,站在什么角度展開這個(gè)機(jī)制的描述,更容易讓大家理解接受棚品。思來(lái)想去靠欢,我覺得還是以一個(gè)Message游歷的形式去描寫,會(huì)顯著有趣和清晰一點(diǎn)铜跑。

Message:

人在邊境X(子線程)服役的士兵Message慵懶得躺在一個(gè)人數(shù)為50(池中最大數(shù)量)的軍營(yíng)(Message池)中掺涛。不料這時(shí)突然接到了上司的obtain()命令(據(jù)說(shuō)obtain命令更加節(jié)省軍費(fèi)),讓他去首都(主線程)告訴中央領(lǐng)導(dǎo)一些神秘代碼疼进。小Message慌亂地整理了下衣角和帽子薪缆,帶上信封,準(zhǔn)備出發(fā)伞广。

上司讓士兵Message收拾完畢之后等待一個(gè)神秘人的電話拣帽,并且囑咐他:到了首都之后,0是這次任務(wù)的暗號(hào)嚼锄。

Message的創(chuàng)建和攜帶信息

Message是消息的載體减拭,Message設(shè)計(jì)成為Parcelable類的派生類,這表明Message可以通過(guò)binder來(lái)跨進(jìn)程發(fā)送区丑。
通常我們都會(huì)用obtain()方法去創(chuàng)建Message拧粪,如果消息池中有Message有修陡,則取出,沒(méi)有可霎,再重新創(chuàng)建魄鸦。這樣可以防止對(duì)象的重復(fù)創(chuàng)建,節(jié)省資源癣朗。

obtain方法源碼

"鈴鈴鈴..."小Message接到了一個(gè)陌生男子的電話拾因。
“我叫handler,來(lái)自activity大本營(yíng)旷余,是你這次任務(wù)的接受者绢记,一會(huì)我?guī)闳ナ锥嫉南⒅行娜?bào)道≌裕”

Handler

來(lái)自Activity大本營(yíng)Handler部門是整個(gè)消息機(jī)制系統(tǒng)的核心部門蠢熄,當(dāng)然部門下有很多個(gè) Handler,這次協(xié)助小Message任務(wù)的叫mHandler炉旷。Handler部門下的員工都有一個(gè)特點(diǎn)签孔,就是只關(guān)心自己的message。

Handler屬于Activity砾跃,創(chuàng)建任何一個(gè)Handler都屬于重寫了Activity中的Handler。

Activity中定義了Handler

在Handler的構(gòu)造中节吮,默認(rèn)完成了對(duì)當(dāng)前線程Looper的綁定抽高,至于Looper是誰(shuí),一會(huì)再談透绩。

Handler的構(gòu)造方法

通過(guò)Looper.myLooper()獲取了當(dāng)前線程保存的Looper實(shí)例翘骂,又通過(guò)mLooper.mQueue獲取了Looper中的MessageQueue實(shí)例。在此時(shí)帚豪,mhandler實(shí)例與looper和messageQueue實(shí)例碳竟,關(guān)聯(lián)上了。

mHandler神情驕傲得對(duì)小Message說(shuō):我已經(jīng)跟首都的消息中心打好了招呼狸臣,準(zhǔn)備接收你了莹桅,現(xiàn)在有兩種車,一種車名叫“send”烛亦,一種叫“post”诈泼,你想坐哪輛去首都都可以,不過(guò)要根據(jù)你上司的命令煤禽,選擇車種類下對(duì)應(yīng)的型號(hào)哦~

  • send

    這里寫圖片描述

  • post

    這里寫圖片描述

從代碼的實(shí)現(xiàn)上來(lái)看铐达,post方法也是在使用send類的方法在發(fā)送消息,只是他們的參數(shù)要求是Runnable對(duì)象檬果。

通過(guò)對(duì)Handler源碼的分析瓮孙,發(fā)現(xiàn)除了sendMessageAtFrontOfQueue方法之外唐断,其余任何send的相關(guān)方法,都經(jīng)過(guò)層層包裝走到了sendMessageAtTime方法中杭抠,我們來(lái)看看源碼:

sendMessageAtTime源碼

這時(shí)小Message和mHandler一同上了車牌號(hào)為“sendMessage”的車脸甘,行駛在一條叫“enqueueMessage”的高速公路上,mHandler向一無(wú)所知的小Message介紹說(shuō)祈争,每個(gè)像他一樣的Message都是通過(guò)enqueueMessage路進(jìn)入MessageQueue的斤程。我們是要去首都的MessageQueue中心,其實(shí)你的消息到時(shí)候也是我處理的菩混,不過(guò)現(xiàn)在還不是時(shí)候哦忿墅,因?yàn)槲液苊Α?/p>

enqueueMessage源碼

enqueueMessage是MessageQueue的方法,用來(lái)將Message根據(jù)時(shí)間排序沮峡,放入到MessageQueue中疚脐。其中msg.target = this,是保證每個(gè)發(fā)送Message的Handler也能處理這個(gè)Message邢疙。

Looper

路上的時(shí)間不短不長(zhǎng)棍弄,mHandler依然為小Message熱心介紹著MessageQueue和Looper
“在每個(gè)駐扎地(線程)中,只有一個(gè)MessageQueue和一個(gè)Looper疟游,他們兩個(gè)是相殺相愛呼畸,同生共死的好基友,Looper是個(gè)跑不死的郵差颁虐,一直負(fù)責(zé)取出MessageQueue中的Message”
"不過(guò)通常只有首都(主線程)的Looper和MessageQueue是創(chuàng)建好的蛮原,其他地方需要我們?nèi)藶榈貏?chuàng)建哦~"

prepare方法

Looper類提供了prepare方法來(lái)創(chuàng)建Looper×砑ǎ可以看到儒陨,當(dāng)重復(fù)創(chuàng)建Looper時(shí),會(huì)拋出異常笋籽,也就是說(shuō)蹦漠,每個(gè)線程只有一個(gè)Looper。

Looper構(gòu)造

緊接著在Looper的構(gòu)造方法中车海,又創(chuàng)建了與它一一對(duì)應(yīng)的MessageQueue笛园,既然Looper在一個(gè)線程中是唯一的,所以MessageQueue也是唯一的侍芝。

在Android中喘沿,ActivityThread的main方法是程序的入口,主線程的Looper和MessageQueue就是在此時(shí)創(chuàng)建的竭贩。

ActivityThread的main方法

可以看到蚜印,在main方法中,既創(chuàng)建了Looper留量,也調(diào)用了Looper.loop()方法窄赋。

mHandler和小Message通過(guò)enqueueMessage路來(lái)到了MessageQueue中哟冬,進(jìn)入之前,門衛(wèi)仔仔細(xì)細(xì)地給小Message貼上了以下標(biāo)簽:
“mHandler負(fù)責(zé)帶入”
“處理時(shí)間為0ms”
并且告訴小Message忆绰,一定要按照時(shí)間順序排隊(duì)浩峡。
進(jìn)入隊(duì)伍中,Looper大哥正在不辭辛勞的將一個(gè)又一個(gè)跟小Message一樣的士兵帶走错敢。

loop方法

分析一下loop方法翰灾,有一個(gè)for的死循環(huán),不斷地調(diào)用queue.next方法稚茅,在消息隊(duì)列中取Message纸淮。并且在Message中取出target,這個(gè)target其實(shí)就是發(fā)送消息的handler亚享,調(diào)用它的dispatchMessage方法咽块。

首都的MessageQueue中心雖然人很多,但是大家都井井有條的排著隊(duì)伍欺税,Looper老哥看了一眼手里的名單侈沪,叫到了小Message的名字,看了一眼小Message身上的標(biāo)簽晚凿,對(duì)他說(shuō):“喔亭罪,又是mHandler帶來(lái)的人啊,那把你交給他處理了”

忐忑不安的小Message看到了一個(gè)熟悉的身影歼秽,mHandler就在面前应役,顯然mHandler有些健忘,可能是接觸了太多跟小Message一樣的人哲银,為了讓mHandler想起自己扛吞,小Message說(shuō)出了上司交給他的暗號(hào)0.

dispatchMessage方法

可以看見dispatchMessage方法中的邏輯比較簡(jiǎn)單呻惕,具體就是如果mCallback不為空荆责,則調(diào)用mCallback的handleMessage()方法,否則直接調(diào)用Handler的handleMessage()方法亚脆,并將消息對(duì)象作為參數(shù)傳遞過(guò)去做院。

在handlerMessage()方法中,小Message出色的完成了自己的任務(wù)濒持。

寫在后面:

下一篇中键耕,我們會(huì)探討一下為什么loop方法中for死循環(huán)不會(huì)造成ANR,有一些有關(guān)Handler的使用技巧柑营,以及可能造成的內(nèi)存泄漏屈雄,敬請(qǐng)期待。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末官套,一起剝皮案震驚了整個(gè)濱河市酒奶,隨后出現(xiàn)的幾起案子蚁孔,更是在濱河造成了極大的恐慌,老刑警劉巖惋嚎,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杠氢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡另伍,警方通過(guò)查閱死者的電腦和手機(jī)鼻百,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摆尝,“玉大人温艇,你說(shuō)我怎么就攤上這事〗衢” “怎么了中贝?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)臼朗。 經(jīng)常有香客問(wèn)我邻寿,道長(zhǎng),這世上最難降的妖魔是什么视哑? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任绣否,我火速辦了婚禮,結(jié)果婚禮上挡毅,老公的妹妹穿的比我還像新娘蒜撮。我一直安慰自己,他們只是感情好跪呈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布段磨。 她就那樣靜靜地躺著,像睡著了一般耗绿。 火紅的嫁衣襯著肌膚如雪苹支。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天误阻,我揣著相機(jī)與錄音债蜜,去河邊找鬼。 笑死究反,一個(gè)胖子當(dāng)著我的面吹牛寻定,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播精耐,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼狼速,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了卦停?” 一聲冷哼從身側(cè)響起向胡,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤浅浮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后捷枯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滚秩,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年淮捆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了郁油。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡攀痊,死狀恐怖桐腌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情苟径,我是刑警寧澤案站,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站棘街,受9級(jí)特大地震影響蟆盐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遭殉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一石挂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧险污,春花似錦痹愚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蚁飒,卻和暖如春动壤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背飒箭。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工狼电, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜒灰,地道東北人弦蹂。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像强窖,于是被迫代替她去往敵國(guó)和親凸椿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,071評(píng)論 25 707
  • 1. ANR異常 Application No Response:應(yīng)用程序無(wú)響應(yīng)翅溺。在主線程中脑漫,是不允許執(zhí)行耗時(shí)的操...
    JackChen1024閱讀 1,382評(píng)論 0 3
  • 異步消息處理線程啟動(dòng)后會(huì)進(jìn)入一個(gè)無(wú)限的循環(huán)體之中髓抑,每循環(huán)一次,從其內(nèi)部的消息隊(duì)列中取出一個(gè)消息优幸,然后回調(diào)相應(yīng)的消息...
    cxm11閱讀 6,424評(píng)論 2 39
  • Android消息處理機(jī)制估計(jì)都被寫爛了,但是依然還是要寫一下碳却,因?yàn)锳ndroid應(yīng)用程序是通過(guò)消息來(lái)驅(qū)動(dòng)的队秩,An...
    一碼立程閱讀 4,469評(píng)論 4 36
  • 昨天去清真寺下面買肉。從外面剛走肉鋪里昼浦,真有些受不了濃郁的膻腥味馍资,但不過(guò)片刻也就習(xí)慣。鋪?zhàn)觾蛇叺陌缸由戏峙胖蠖训?..
    鉛筆芒種閱讀 598評(píng)論 0 1