Android學(xué)習(xí)筆記15 多線程編程之Handler異步消息處理

異步消息處理機(jī)制,Android開發(fā)中必須要掌握的內(nèi)容扇商。作為面試中經(jīng)常會(huì)提問的知識(shí)點(diǎn),同時(shí)也是開發(fā)中經(jīng)常要打交道的部分宿礁,我們必須要對(duì)它透徹地研究案铺,明白實(shí)現(xiàn)原理,熟悉整個(gè)流程窘拯,以及Handler红且、Message、Looper等不同角色之間的關(guān)系涤姊。下面是自己對(duì)于這部分的學(xué)習(xí)總結(jié)暇番。

一、背景介紹
二思喊、異步消息處理機(jī)制概述
三壁酬、示例與解析
四、Handler詳解
五、Message與MessageQueue詳解
六舆乔、Looper詳解
七岳服、異步消息處理機(jī)制總結(jié)

一、背景介紹

這部分是背景介紹希俩,我們用過Handler吊宋,用過Message,可能了解過Looper等等颜武,但是Android為什么要有這一套異步消息處理機(jī)制璃搜?

當(dāng)一個(gè)應(yīng)用啟動(dòng)時(shí),如果它沒有其它組件在運(yùn)行的話鳞上,Android系統(tǒng)會(huì)為這個(gè)應(yīng)用創(chuàng)建一個(gè)Linux進(jìn)程这吻,進(jìn)程里有一個(gè)執(zhí)行線程(這個(gè)執(zhí)行線程叫主線程,也稱為UI線程)篙议。默認(rèn)情況下唾糯,同一個(gè)應(yīng)用的所有組件都運(yùn)行在相同的進(jìn)程和線程里。

系統(tǒng)絕對(duì)不會(huì)為一個(gè)組件的實(shí)例創(chuàng)建單獨(dú)的線程鬼贱。運(yùn)行于同一進(jìn)程的所有組件均在 UI 線程中實(shí)例化移怯,并且對(duì)每個(gè)組件的系統(tǒng)調(diào)用均由該線程進(jìn)行分發(fā)。因此吩愧,響應(yīng)系統(tǒng)回調(diào)的方法(例如芋酌,報(bào)告用戶操作的 onKeyDown()方法或生命周期回調(diào)方法)始終在進(jìn)程的 UI 線程中運(yùn)行。

Android 的單線程模式有兩個(gè)務(wù)必要遵守的原則雁佳,一是不要阻塞 UI 線程,二是不要在 UI 線程之外訪問Android這些界面UI元素同云。

到這里糖权,我們應(yīng)該初步了解了什么是UI線程,Android的單線程模式炸站。但是星澳,在開發(fā)過程中我們可能會(huì)有這樣的需要,想要聯(lián)網(wǎng)獲取數(shù)據(jù)旱易,然后將數(shù)據(jù)顯示到界面上禁偎,因?yàn)槁?lián)網(wǎng)獲取數(shù)據(jù)是個(gè)比較耗時(shí)的任務(wù),為了不阻塞UI線程阀坏,我們需要?jiǎng)?chuàng)建一個(gè)新的線程來進(jìn)行數(shù)據(jù)獲取如暖,但是這樣就會(huì)造成數(shù)據(jù)獲取到的時(shí)候不是在UI線程里的,我們不能直接在非UI線程里更新界面忌堂,所以要把數(shù)據(jù)發(fā)送到UI線程里盒至。怎么發(fā)送呢?這時(shí),異步消息處理機(jī)制就出現(xiàn)了枷遂。

二樱衷、異步消息處理機(jī)制概述

想要搞清楚異步消息處理機(jī)制,我們必須要搞清楚它的幾個(gè)關(guān)鍵知識(shí)點(diǎn)之間的關(guān)系酒唉,包括Handler矩桂、Message、MessageQueue痪伦、Looper耍鬓。

下面先對(duì)整個(gè)流程總體上有個(gè)把握。我們以上面提到的聯(lián)網(wǎng)獲取數(shù)據(jù)的需求為例流妻。

首先牲蜀,我們想要聯(lián)網(wǎng)獲取數(shù)據(jù),考慮到聯(lián)網(wǎng)獲取數(shù)據(jù)可能是個(gè)耗時(shí)的任務(wù)绅这,我們需要開啟一個(gè)新的線程涣达,在其中完成操作。開啟線程的方法有好幾種证薇,但是經(jīng)常使用的是先定義一個(gè)實(shí)現(xiàn)接口Runable的類度苔,然后在方法run()里訪問網(wǎng)絡(luò)數(shù)據(jù),最后在需要的地方實(shí)例化Thread對(duì)象浑度,傳入之前定義的類的實(shí)例寇窑,調(diào)用方法start()來開啟線程。

上一步我們?cè)趓un方法里訪問網(wǎng)絡(luò)箩张,獲取到數(shù)據(jù)后需要把它發(fā)送到UI線程里甩骏。怎么發(fā)送,在創(chuàng)建線程之前先慷,我們先實(shí)例化一個(gè)Handler饮笛。這樣在run方法里我們可以通過實(shí)例化的Handler把數(shù)據(jù)發(fā)送到UI線程里,數(shù)據(jù)在發(fā)送過程中要用Message作為載體论熙。

Handler福青,直接翻譯就是處理器,它可以發(fā)送Message脓诡,也可以處理Message无午。在UI線程里實(shí)例化Handler,然后可以利用它把非UI線程里的Message發(fā)送到Message Queue里祝谚,之后它可以對(duì)從Message Queue里出來的消息進(jìn)行處理宪迟。

Looper,在平時(shí)的使用中我們一般會(huì)與Handler踊跟、Message打交道比較多踩验,但是Looper是背后運(yùn)行不可或缺的一個(gè)部分鸥诽,它的作用主要是不斷從MessageQueue中取出Message,分發(fā)給Handler箕憾,讓Handler處理牡借。

三、示例與解析

下面看一個(gè)例子袭异,可以很直觀地了解Handler和Message的用法钠龙。

  1. 實(shí)例化Handler。重寫handleMessage()方法完成對(duì)消息的處理御铃。


    Handler實(shí)例化
  2. 創(chuàng)建線程碴里。這里實(shí)現(xiàn)run方法,網(wǎng)絡(luò)請(qǐng)求如果不熟悉可以暫時(shí)忽略上真,重點(diǎn)看畫出來的部分咬腋,獲取到數(shù)據(jù)后先實(shí)例化好Message 最后通過Handler的sendMessage方法發(fā)送。


    Paste_Image.png
  3. 開啟線程


    開啟線程

四睡互、Handler詳解

1根竿、概述

前面已經(jīng)提到,Handler可以譯為處理器就珠,它的主要作用有兩個(gè)寇壳,一是定時(shí)執(zhí)行Message和Runnable,二是讓一個(gè)執(zhí)行動(dòng)作進(jìn)入隊(duì)列妻怎,在另外的線程里執(zhí)行壳炎。

Handler允許我們發(fā)送和處理與線程的相關(guān)的Message以及Runnable對(duì)象。每個(gè)Handler實(shí)例都關(guān)聯(lián)著一個(gè)單線程逼侦,同時(shí)也關(guān)聯(lián)著那個(gè)線程的消息隊(duì)列匿辩。當(dāng)我們創(chuàng)建一個(gè)新的Handler時(shí),它就與創(chuàng)建它的線程以及線程的消息隊(duì)列相關(guān)聯(lián)偿洁,也就是說撒汉,它將會(huì)傳遞Message和Runnable給MessageQueue,并在它們從MessageQueue出來時(shí)執(zhí)行它們涕滋。

2、用法

Handler的第一個(gè)作用是定時(shí)執(zhí)行Message和Runnable挠阁,常用方法:

方法 簡介
handleMessage(Message) 對(duì)接收到的消息進(jìn)行處理
post(Runnable) 讓Runnable對(duì)象入隊(duì)
postAtTime(Runnable, long) 指定的時(shí)間入隊(duì)
postDelayed(Runnable, long) 延長指定的時(shí)間入隊(duì)
sendEmptyMessage(int) 發(fā)送只有標(biāo)識(shí)的消息
sendMessage(Message) 發(fā)送消息
sendMessageAtTime(Message, long) 指定的時(shí)間發(fā)送消息
sendMessageDelayed(Message, long) 延長指定的時(shí)間發(fā)送消息
  1. handleMessage方法主要用于處理消息宾肺,我們創(chuàng)建Handler時(shí)通常重寫此方法。
  1. post系列的方法允許我們對(duì)Runnable對(duì)象執(zhí)行入隊(duì)操作(當(dāng)它們被接收到的時(shí)候進(jìn)入MessageQueue)侵俗。
  2. sendMessage系列方法允許我們對(duì)Message對(duì)象執(zhí)行入隊(duì)操作锨用,Message對(duì)象包含數(shù)據(jù),這些數(shù)據(jù)將會(huì)在Handler的handleMessage(Message)方法中被處理隘谣。

五增拥、Message與MessageQueue詳解

1啄巧、Message

我們?cè)谛碌木€程里獲取到數(shù)據(jù)時(shí),首先把數(shù)據(jù)給封裝到Message里掌栅,再利用Handler來發(fā)送秩仆。Message是一個(gè)可以被發(fā)送給Handler,包含標(biāo)識(shí)和數(shù)據(jù)的對(duì)象猾封。Message對(duì)象包含兩個(gè)額外的int類型變量和一個(gè)額外的對(duì)象澄耍,利用它們大多數(shù)情況下我們不用再做內(nèi)存分配相關(guān)工作。實(shí)例化Message最好的方法是調(diào)用Message.obtain()或Handler.obtainMessage()晌缘,因?yàn)檫@兩個(gè)方法是從一個(gè)可回收利用的對(duì)象池中獲取Message的齐莲。

2、MessageQueue

MessageQueue就是Looper分發(fā)的Message集合磷箕,Message不是直接添加到MessageQueue中的选酗,而是通過與Looper關(guān)聯(lián)的Handler對(duì)象來完成的。我們可以使用方法Looper.myQueue()來獲取當(dāng)前線程的MessageQueue岳枷。

六芒填、Looper詳解

Looper,就是用于運(yùn)行一個(gè)線程的消息隊(duì)列的類嫩舟。默認(rèn)情況下線程沒有關(guān)聯(lián)的消息隊(duì)列氢烘,可以通過調(diào)用方法prepare()來創(chuàng)建隊(duì)列,然后調(diào)用loop()來不斷分發(fā)消息直到都處理完家厌。下面可以看看Looper類解析播玖。

  • 首先是構(gòu)造函數(shù)。 可以看到Looper初始化時(shí)創(chuàng)建了一個(gè)消息隊(duì)列饭于,MessageQueue蜀踏,同時(shí)關(guān)聯(lián)了當(dāng)前線程。
Looper的構(gòu)造函數(shù)
  • 之后是prepare()方法掰吕。
    這里我們重點(diǎn)關(guān)注一下果覆,ThreadLocal類,它是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類殖熟,通過它可以在指定的線程中存儲(chǔ)數(shù)據(jù)局待,數(shù)據(jù)存儲(chǔ)以后,只有在指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù)菱属,對(duì)于其它線程來說無法獲取到數(shù)據(jù)钳榨。
    prepare方法主要是先判斷是否已經(jīng)有Looper實(shí)例,如果有就拋出錯(cuò)誤纽门,如果沒有就實(shí)例化Looper并存儲(chǔ)薛耻。
Looper的prepare()方法
  • 最后我們看下loop()函數(shù)。這個(gè)函數(shù)主要是不斷地從消息隊(duì)列中取出消息赏陵,并分發(fā)給消息關(guān)聯(lián)的Handler饼齿∷茄可以看到,方法內(nèi)部先獲取Looper實(shí)例缕溉,再獲取消息隊(duì)列考传,之后是個(gè)死循環(huán),不斷從隊(duì)列中取出消息并分發(fā)給Message的target倒淫,即Handler伙菊。
Looper的loop()方法

七、異步消息處理機(jī)制總結(jié)

綜合上面的內(nèi)容敌土,相信大家對(duì)整個(gè)機(jī)制已經(jīng)了解的差不多了镜硕。最后我們以Android開發(fā)藝術(shù)探索作者任玉剛的一段話作總結(jié)。

異步消息處理機(jī)制主要是指Handler的運(yùn)行機(jī)制返干,Handler的運(yùn)行需要底層的MessageQueue和Looper的支撐兴枯。

MessageQueue的中文翻譯是消息隊(duì)列,顧名思義它的內(nèi)部存儲(chǔ)了一組消息矩欠,其以隊(duì)列的形式對(duì)外提供插入和刪除的工作财剖,雖然叫做消息隊(duì)列,但是它的內(nèi)部存儲(chǔ)結(jié)構(gòu)并不是真正的隊(duì)列癌淮,而是采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)消息列表躺坟。

Looper的中文翻譯為循環(huán),在這里可以理解為消息循環(huán)乳蓄,由于MessageQueue只是一個(gè)消息的存儲(chǔ)單元咪橙,它不能去處理消息,而Looper就填補(bǔ)了這個(gè)功能虚倒,Looper會(huì)以無限循環(huán)的形式去查找是否有新消息美侦,如果有的話就處理消息,否則就一直等待著魂奥。

Looper中還有一個(gè)特殊的概念菠剩,那就是ThreadLocal,ThreadLocal并不是線程耻煤,它的作用是可以在每個(gè)線程中存儲(chǔ)數(shù)據(jù)具壮。大家知道,Handler創(chuàng)建的時(shí)候會(huì)采用當(dāng)前線程的Looper來構(gòu)造消息循環(huán)系統(tǒng)哈蝇,那么Handler內(nèi)部如何獲取到當(dāng)前線程的Looper呢嘴办?這就要使用ThreadLocal了,ThreadLocal可以在不同的線程之中互不干擾地存儲(chǔ)并提供數(shù)據(jù)买鸽,通過ThreadLocal可以輕松獲取每個(gè)線程的Looper。

當(dāng)然需要注意的是贯被,線程是默認(rèn)沒有Looper的眼五,如果需要使用Handler就必須為線程創(chuàng)建Looper妆艘。大家經(jīng)常提到的主線程,也叫UI線程看幼,它就是ActivityThread批旺,ActivityThread被創(chuàng)建時(shí)就會(huì)初始化Looper,這也是在主線程中默認(rèn)可以使用Handler的原因诵姜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汽煮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子棚唆,更是在濱河造成了極大的恐慌暇赤,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宵凌,死亡現(xiàn)場(chǎng)離奇詭異鞋囊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瞎惫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門溜腐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓜喇,你說我怎么就攤上這事挺益。” “怎么了乘寒?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵望众,是天一觀的道長。 經(jīng)常有香客問我肃续,道長黍檩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任始锚,我火速辦了婚禮刽酱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瞧捌。我一直安慰自己棵里,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布姐呐。 她就那樣靜靜地躺著殿怜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曙砂。 梳的紋絲不亂的頭發(fā)上头谜,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音鸠澈,去河邊找鬼柱告。 笑死截驮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的际度。 我是一名探鬼主播葵袭,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼乖菱!你這毒婦竟也來了坡锡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤窒所,失蹤者是張志新(化名)和其女友劉穎鹉勒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體墩新,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贸弥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了海渊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绵疲。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖臣疑,靈堂內(nèi)的尸體忽然破棺而出盔憨,到底是詐尸還是另有隱情,我是刑警寧澤讯沈,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布郁岩,位于F島的核電站,受9級(jí)特大地震影響缺狠,放射性物質(zhì)發(fā)生泄漏问慎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一挤茄、第九天 我趴在偏房一處隱蔽的房頂上張望如叼。 院中可真熱鬧,春花似錦穷劈、人聲如沸笼恰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽社证。三九已至,卻和暖如春评凝,著一層夾襖步出監(jiān)牢的瞬間追葡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辽俗,地道東北人疾渣。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像崖飘,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杈女,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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