常州信息職業(yè)技術(shù)學(xué)院 解志君
Android的UI界面更新在多線程并發(fā)的環(huán)境下是不安全的,因此Android要求對(duì)UI界面的更新必須在UI線程(即主線程)中進(jìn)行。如果在非UI線程中進(jìn)行界面更新,系統(tǒng)會(huì)提示錯(cuò)誤,這就是Android的單線程模型。但這是否意味著Android應(yīng)用中只能有一個(gè)線程呢,答案是否定的。在Android中,除了UI線程外,還存在著工作線程,工作線程的作用是處理耗時(shí)的業(yè)務(wù)邏輯。這是因?yàn)楹臅r(shí)的業(yè)務(wù)邏輯不能放在UI線程中,因?yàn)槟菢雍芸赡茏枞鸘I線程,從而延遲程序?qū)τ脩舨僮鞯捻憫?yīng),如果用戶的操作在20s內(nèi)得不到響應(yīng),系統(tǒng)會(huì)提示用戶ANR(Application Not Responsible)信息[1]。但是工作線程和UI線程又不是完全獨(dú)立的,有時(shí)候又要根據(jù)工作線程處理的結(jié)果來(lái)更新UI界面,但是更新UI界面不能在工作線程中進(jìn)行,那么,工作線程又是如何通知UI線程來(lái)更新UI界面的呢?這就涉及到Android的消息循環(huán)機(jī)制。
Android的消息循環(huán)機(jī)制是事件處理的一種形式,它主要是為了解決Android線程間的通信問題。因此,深入理解Android系統(tǒng)的消息循環(huán)機(jī)制對(duì)于從更深層次上把握Android應(yīng)用程序的運(yùn)行機(jī)制是大有裨益的。其實(shí)Google在設(shè)計(jì)Android系統(tǒng)的消息循環(huán)時(shí)是參考了Windows程序的消息循環(huán)機(jī)制的,即Android應(yīng)用程序的消息處理機(jī)制也是由消息循環(huán)、消息發(fā)送和消息處理三部分構(gòu)成的。消息循環(huán)是指發(fā)送給每個(gè)線程的消息都被按處理時(shí)間的先后順序存入該線程的消息隊(duì)列MessageQueue中,然后該線程的循環(huán)器Looper不斷的從該隊(duì)列中依次取出每條消息進(jìn)行處理,若消息隊(duì)列為空,則該線程處于空閑等待狀態(tài);消息發(fā)送是指把消息發(fā)送到需要處理該消息的線程的消息隊(duì)列中,也就是需要與當(dāng)前線程進(jìn)行通信的線程的消息隊(duì)列中,Android中一般是通過(guò)Handler的相關(guān)方法來(lái)進(jìn)行消息發(fā)送的;消息處理是指消息被從消息隊(duì)列中取出時(shí),調(diào)用Handler的相關(guān)方法,一般是handleMessage()方法來(lái)對(duì)消息進(jìn)行處理。理解Android的消息循環(huán)機(jī)制需要把握以下幾點(diǎn):
(1)每個(gè)線程都有一個(gè)唯一的消息循環(huán)器Looper,它扮演著MessageQueue和Handler之間橋梁的角色,它源源不斷的依次從MessageQueue中取出消息,并將消息分發(fā)到指定的處理者Handler對(duì)象進(jìn)行處理[2]。
(2)每個(gè)Looper都封裝了一個(gè)消息隊(duì)列MessageQueue,它是一個(gè)FIFO的隊(duì)列,用來(lái)存儲(chǔ)該Looper所關(guān)聯(lián)的線程的消息。
(3)每個(gè)Handler在創(chuàng)建時(shí)都被綁定到一個(gè)Looper,它是消息的發(fā)送者和處理者,它把消息發(fā)送到與它綁定的Looper的消息隊(duì)列中,同時(shí),該消息的處理者就被設(shè)置為該Handler對(duì)象。當(dāng)Looper從MessageQueue中循環(huán)到該條消息時(shí),消息的處理者Handler對(duì)象的handleMessage()方法就會(huì)被自動(dòng)調(diào)用,以處理該消息。
(4)Message是消息類,它封裝了消息的相關(guān)內(nèi)容,它的target屬性指明了該消息的處理者。下圖形象的說(shuō)明了Handler的消息循環(huán)過(guò)程。
圖1 Android消息循環(huán)機(jī)制示意圖
為了方便UI線程的消息事件處理,在Android應(yīng)用程序的UI線程被創(chuàng)建時(shí),系統(tǒng)會(huì)自動(dòng)為UI線程創(chuàng)建一個(gè)消息循環(huán)器Looper,該類中封裝了一個(gè)MessageQueue的成員變量。也就是說(shuō),主線程在創(chuàng)建后就自動(dòng)具有了消息循環(huán)器和消息隊(duì)列,開發(fā)人員只需將需要處理的消息發(fā)送至UI線程的消息隊(duì)列中。而在自定義的工作線程中,系統(tǒng)是不會(huì)自動(dòng)為工作線程創(chuàng)建Looper和MessageQueue的,必須由程序員自己創(chuàng)建工作線程的Looper和MessageQueue。使用Android提供的API,可以非常容易做到這一點(diǎn)。
要為工作線程建立消息循環(huán),只需要四個(gè)步驟:
(1)生成工作線程的Looper,通過(guò)調(diào)用Looper.prepare()方法來(lái)實(shí)現(xiàn)。
(2)將Handler與工作線程的Looper的綁定,通過(guò)在工作線程中創(chuàng)建Handler對(duì)象來(lái)實(shí)現(xiàn),因?yàn)镠andler在默認(rèn)情況下是與創(chuàng)建它的線程的Looper綁定的,否則就需要在創(chuàng)建Handler時(shí)指定其構(gòu)造方法的Looper參數(shù)來(lái)顯式指定該Handler與哪個(gè)線程的Looper綁定。
(3)定義消息處理方法,通過(guò)重寫Handler的handleMessage()方法實(shí)現(xiàn)。
(4)啟動(dòng)消息循環(huán),通過(guò)調(diào)用Looper.loop()方法實(shí)現(xiàn)。
(5)結(jié)束消息循環(huán),通過(guò)調(diào)用Looper.quit()方法實(shí)現(xiàn)。
另外需要說(shuō)明的是,Android為了開發(fā)人員的方便,也提供了一個(gè)帶有消息循環(huán)的線程類HandlerThread,開發(fā)人員可以直接使用該類來(lái)創(chuàng)建工作線程,使用該類創(chuàng)建的線程會(huì)自動(dòng)具有循環(huán)器Looper和消息隊(duì)列MessageQueue。
下面的例子每隔1秒時(shí)間將界面上文本的顏色更改為一種隨機(jī)生成的顏色。這個(gè)功能使用Android消息循環(huán)是容易實(shí)現(xiàn)的。只需要在工作線程中每隔1秒向UI線程發(fā)送一條消息,UI線程處理這條,實(shí)現(xiàn)文本顏色的改變。下面來(lái)看一看實(shí)現(xiàn)這一功能的具體實(shí)現(xiàn)。
4.1 在工作線程中周期性的發(fā)送消息到UI線程的消息隊(duì)列
要在工作線程中周期性的向UI線程的消息隊(duì)列發(fā)送消息,需要在工作線程中使用一個(gè)循環(huán),可以通過(guò)一個(gè)標(biāo)志變量來(lái)控制該循環(huán)的開始與停止;然后使用UI線程中創(chuàng)建的Handler對(duì)象的相關(guān)方法生成消息并發(fā)送至UI線程的消息隊(duì)列;接著讓工作線程休息1秒鐘。這樣工作線程就以周期為1秒的間隔不斷的向UI線程發(fā)送消息,直到工作線程停止。該工作線程的開啟可在Activity的onCreate()方法中來(lái)完成。下面是工作線程的實(shí)現(xiàn)代碼,代碼中的注釋對(duì)相關(guān)方法的功能進(jìn)行了說(shuō)明。
Thread t=new Thread(new Runnable(){//線程參數(shù)是一個(gè)匿名內(nèi)部類
@Override
public void run(){
while(!Thread.currentThread().isInterrupted()){//線程循環(huán)
Message msg=handler.obtain Message();//生成消息
msg.what=0x110;//設(shè)置消息標(biāo)識(shí),即Message的what屬性
handler.sendMessage(msg);//發(fā)送消息
try{
Thread.sleep(1000);//線程休眠1秒,實(shí)現(xiàn)周期性發(fā)送消息
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
});
4.2 在UI線程中處理工作線程發(fā)送過(guò)來(lái)的消息
消息的處理是由Handler來(lái)完成的。要顯示消息的處理,需要在UI線程中創(chuàng)建Handler時(shí)重寫Handler的handlerMessage()方法,該方法定義了消息處理邏輯[3]。下面是在UI線程中創(chuàng)建Handler的代碼,代碼中注釋解釋了相關(guān)方法的功能。
//該Handler必須在UI線程中創(chuàng)建,以使之與UI線程關(guān)聯(lián)。否則需要使用帶Looper參數(shù)的
//造訪方法來(lái)創(chuàng)建Handler
Handler handler=new Handler(){//內(nèi)部類形式定義的Handler對(duì)象
@Override
public void handleMessage(Message msg){//重寫消息處理方法
if(msg.what==0x110){//通過(guò)消息表示區(qū)分消息
int red=new Random().nextInt(255);
int green=new Random().nextInt(255);
int blue=new Random().nextInt(255);
int color=Color.rgb(red,green,blue);//生成隨機(jī)顏色
txt.setTextColor(color);//設(shè)置文本顏色
}
}
};
Android的消息循環(huán)機(jī)制是Android事件處理的基石,所以從本質(zhì)上說(shuō),Android程序的運(yùn)行機(jī)制是基于消息循環(huán)的,也就是說(shuō),Android程序是消息驅(qū)動(dòng)的。在Android框架的實(shí)現(xiàn)源碼中,消息處理代碼隨處可見。當(dāng)然,在我們實(shí)際的應(yīng)用編程中,消息處理也應(yīng)用得非常廣泛,比如工作線程通知主線程更新UI、游戲中周期性更新UI、線程間通信等??傊?,Android的消息循環(huán)在Android框架中占有非常重要的地位,深入理解Android的消息循環(huán)機(jī)制,對(duì)于提升程序員的Android程序編制水平是非常有幫助的。
[1]李剛.瘋狂Android講義[M].北京:電子工業(yè)出版社,2013.
[2]高洪巖.Android學(xué)習(xí)精要[M].北京:清華大學(xué)出版社,2012.
[3]王國(guó)輝,李偉等.Android開發(fā)寶典[M].北京:機(jī)械工業(yè)出版社,2012.