一忧侧、概述
? ? ? 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)用的方法