解析異步消息處理機(jī)制

一忧侧、概述

? ? ? Android異步消息處理機(jī)制主要由四個(gè)部分組成标捺,Message、Handle乎完、MessageQueue和Looper熏兄。下面我就對(duì)這四個(gè)部分進(jìn)行一下簡(jiǎn)要的介紹。

1.Message

? ? ?Message是在線程之間傳遞的消息树姨,它可以在內(nèi)部攜帶少量的信息摩桶,用于在不同線程之間交換數(shù)據(jù)。

2.MessageQueue

? ? ? ?MessageQueue 是消息隊(duì)列帽揪,它主要用于存放所有由 Handler 發(fā)送過(guò)來(lái)的消息硝清,這部分消息會(huì)一直在消息隊(duì)列中,等待被處理转晰。每個(gè)線程中只會(huì)有一個(gè) MessageQueue 對(duì)象芦拿。

3.Handle

? ? ? ? Handler 顧名思義也就是處理者的意思,它主要用于發(fā)送和處理消息查邢。 發(fā)送消息一般使用 handler 的 sendMessage()方法蔗崎,處理消息會(huì)調(diào)用 handleMessage() 方法。

4.Looper

? ? ? Looper 是每個(gè)線程中 MessageQueue 的管家侠坎, 調(diào)用 loop() 方法后蚁趁,就會(huì)進(jìn)入到一個(gè)無(wú)限循環(huán)當(dāng)中,然后每當(dāng)發(fā)現(xiàn) MessageQueue 中存在一條消息实胸,就會(huì)將其取出他嫡,并傳遞到 handleMessage()方法當(dāng)中番官。每個(gè)線程中也只會(huì)有一個(gè)Looper對(duì)象。

? ? ? 了解了Message钢属、Handle徘熔、MessageQueue以及Looper的基本概念之后,我們?cè)賮?lái)對(duì)異步消息處理的整個(gè)過(guò)程梳理一遍淆党。首先需要在主線程當(dāng)中創(chuàng)建一個(gè)Handle對(duì)象酷师,并重寫(xiě)handleMessage()方法。然后當(dāng)子線程中需要進(jìn)行UI操作時(shí)染乌,就創(chuàng)建一個(gè)Message對(duì)象山孔,并通過(guò)Handle將這條消息發(fā)送出去。之后這條信息會(huì)被添加到MessageQueue的隊(duì)列中等待被處理荷憋,而Looper則會(huì)一直嘗試從MessageQueue中取出待處理消息台颠,最后分發(fā)回Handle的handleMessage()方法中。由于Handle是在主線程中創(chuàng)建的勒庄,所以此時(shí)handleMessage()方法中的代碼也會(huì)在主線程中運(yùn)行串前,于是我們?cè)谶@里就可以安心地進(jìn)行UI操作了。

整個(gè)異步消息處理機(jī)制的流程示意圖如圖所示实蔽。


? ? ? ? 一條Message經(jīng)過(guò)這樣一個(gè)流程的輾轉(zhuǎn)調(diào)用后荡碾,也就是從子線程進(jìn)入到主線程,從不能更新UI變成了可以更新UI局装,整個(gè)異步消息處理機(jī)制的核心思想也就是如此了坛吁。

二、詳細(xì)介紹

1贼邓、Looper

對(duì)于Looper主要是prepare()和loop()兩個(gè)方法阶冈。

publicstaticfinalvoidprepare()

{

? ? if(sThreadLocal.get() !=null)

? ? {

? ? ? ? ?thrownewRuntimeException("Only one Looper may be created per thread");

? ? }

? ? sThreadLocal.set(newLooper(true));

}

sThreadLocal是一個(gè)ThreadLocal對(duì)象,可以在一個(gè)線程中存儲(chǔ)變量塑径。Looper 就是存儲(chǔ)在sThreadLocal里面。這個(gè)方法被調(diào)用后填具,首先會(huì)判斷當(dāng)前線程里面有沒(méi)有 Looper對(duì)象统舀,如果沒(méi)有就會(huì)創(chuàng)建一個(gè) Looper 對(duì)象,如果存在則會(huì)拋出異常劳景∮颍可見(jiàn),prepare()方法盟广,不能被調(diào)用兩次闷串。這就保證了一個(gè)線程只有一個(gè)Looper對(duì)象。

接下來(lái)我們看一下Looper的構(gòu)造函數(shù):

privateLooper(booleanquitAllowed)

{

mQueue=newMessageQueue(quitAllowed);

mRun=true;

mThread=Thread.currentThread();

}

在 Looper 的構(gòu)造函數(shù)中筋量,創(chuàng)建了 MessageQueue 對(duì)象烹吵,這也保證了一個(gè)線程只有一個(gè) MessageQueue 對(duì)象碉熄。

然后我們看看 loop() 方法:

publicstaticvoidloop()

{

finalLooper me =myLooper();

if(me ==null)

{

? ? ? ?thrownewRuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

}

? ? ? ? ?finalMessageQueue queue =me.mQueue;

//Make sure the identity of this thread is that of the local process,

//and keep track of what that identity token actually is.Binder.clearCallingIdentity();finallongident =Binder.clearCallingIdentity();

for(;;)

{

? ? ? Message msg= queue.next();//might blockif(msg ==null) {//No message indicates that the message queue is quitting.return;

}

//This must be in a local variable, in case a UI event sets the loggerPrinter

logging =me.mLogging;if(logging !=null) {

logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback+ ": " +msg.what);

}

msg.target.dispatchMessage(msg);if(logging !=null) {

logging.println("<<<<< Finished to " + msg.target + " " +msg.callback);

}//Make sure that during the course of dispatching the//identity of the thread wasn't corrupted.finallongnewIdent =Binder.clearCallingIdentity();if(ident !=newIdent) {

Log.wtf(TAG,"Thread identity changed from 0x" ? ? ? ? ? ? ? ? ? ? ? ?+ Long.toHexString(ident) + " to 0x" ? ? ? ? ? ? ? ? ? ? ? ?+ Long.toHexString(newIdent) + " while dispatching to " ? ? ? ? ? ? ? ? ? ? ? ?+ msg.target.getClass().getName() + " " ? ? ? ? ? ? ? ? ? ? ? ?+ msg.callback + " what=" +msg.what);

}

msg.recycle();

}

}

這個(gè)方法先調(diào)用 myLooper() 方法,得到 sThreadLocal 中保存的 Looper 對(duì)象肋拔,并得到 looper 對(duì)象對(duì)應(yīng)的 MessageQueue 對(duì)象锈津,然后就進(jìn)入無(wú)限循環(huán)。

該循環(huán)主要包括:取出一條消息凉蜂,如果沒(méi)有消息則阻塞琼梆; 調(diào)用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。

Looper主要作用:

1窿吩、 與當(dāng)前線程綁定茎杂,保證一個(gè)線程只會(huì)有一個(gè)Looper實(shí)例,同時(shí)一個(gè)Looper實(shí)例也只有一個(gè)MessageQueue纫雁。

2煌往、 loop()方法,不斷從MessageQueue中去取消息先较,交給消息的target屬性的dispatchMessage去處理携冤。

2、Handler

在使用Handler之前闲勺,我們都是初始化一個(gè)實(shí)例曾棕,比如用于更新UI線程,我們會(huì)在聲明的時(shí)候直接初始化菜循,或者在onCreate中初始化Handler實(shí)例

'''privateHandler mHandler =newHandler()

{publicvoidhandleMessage(android.os.Message msg)

{switch(msg.what)

{casevalue:break;default:break;

}

};

};'''

三翘地、小結(jié)

1、首先Looper.prepare()在本線程中保存一個(gè)Looper實(shí)例癌幕,然后該實(shí)例中保存一個(gè)MessageQueue對(duì)象衙耕;因?yàn)長(zhǎng)ooper.prepare()在一個(gè)線程中只能調(diào)用一次,所以MessageQueue在一個(gè)線程中只會(huì)存在一個(gè)勺远。大家可能還會(huì)問(wèn)橙喘,那么在Activity中,我們并沒(méi)有顯示的調(diào)用Looper.prepare()和Looper.loop()方法胶逢,為啥Handler可以成功創(chuàng)建呢厅瞎,這是因?yàn)樵贏ctivity的啟動(dòng)代碼中,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法

2初坠、Looper.loop()會(huì)讓當(dāng)前線程進(jìn)入一個(gè)無(wú)限循環(huán)和簸,不端從MessageQueue的實(shí)例中讀取消息,然后回調(diào)msg.target.dispatchMessage(msg)方法碟刺。

3锁保、Handler的構(gòu)造方法,會(huì)首先得到當(dāng)前線程中保存的Looper實(shí)例,并與Looper實(shí)例中的MessageQueue相關(guān)聯(lián)爽柒。

4吴菠、Handler的sendMessage方法,會(huì)給msg的target賦值為handler自身霉赡,然后加入MessageQueue中橄务。

5、在構(gòu)造Handler實(shí)例時(shí)穴亏,我們會(huì)重寫(xiě)handleMessage方法蜂挪,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法

CSDN原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嗓化,隨后出現(xiàn)的幾起案子棠涮,更是在濱河造成了極大的恐慌,老刑警劉巖刺覆,帶你破解...
    沈念sama閱讀 211,496評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件严肪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谦屑,警方通過(guò)查閱死者的電腦和手機(jī)驳糯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,187評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)氢橙,“玉大人酝枢,你說(shuō)我怎么就攤上這事『肥郑” “怎么了帘睦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,091評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坦康。 經(jīng)常有香客問(wèn)我竣付,道長(zhǎng),這世上最難降的妖魔是什么滞欠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,458評(píng)論 1 283
  • 正文 為了忘掉前任古胆,我火速辦了婚禮,結(jié)果婚禮上筛璧,老公的妹妹穿的比我還像新娘赤兴。我一直安慰自己,他們只是感情好隧哮,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,542評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著座舍,像睡著了一般沮翔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,802評(píng)論 1 290
  • 那天采蚀,我揣著相機(jī)與錄音疲牵,去河邊找鬼。 笑死榆鼠,一個(gè)胖子當(dāng)著我的面吹牛纲爸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妆够,決...
    沈念sama閱讀 38,945評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼识啦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了神妹?” 一聲冷哼從身側(cè)響起颓哮,我...
    開(kāi)封第一講書(shū)人閱讀 37,709評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸵荠,沒(méi)想到半個(gè)月后冕茅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,158評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛹找,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,502評(píng)論 2 327
  • 正文 我和宋清朗相戀三年姨伤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庸疾。...
    茶點(diǎn)故事閱讀 38,637評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乍楚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出彼硫,到底是詐尸還是另有隱情炊豪,我是刑警寧澤,帶...
    沈念sama閱讀 34,300評(píng)論 4 329
  • 正文 年R本政府宣布拧篮,位于F島的核電站词渤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏串绩。R本人自食惡果不足惜缺虐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,911評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望礁凡。 院中可真熱鬧高氮,春花似錦、人聲如沸顷牌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,744評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窟蓝。三九已至罪裹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背状共。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,982評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工套耕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人峡继。 一個(gè)月前我還...
    沈念sama閱讀 46,344評(píng)論 2 360
  • 正文 我出身青樓冯袍,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親碾牌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子康愤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,500評(píng)論 2 348

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