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