王 濤
(江蘇廣播電視大學(xué)武進(jìn)學(xué)院,江蘇常州213149)
在移動互聯(lián)網(wǎng)時代,移動終端的地位變得越來越重要,智能手機(jī)已成為人們?nèi)粘I钪械谋匦杵??;谝苿釉O(shè)備操作系統(tǒng)的開發(fā)是現(xiàn)在程序開發(fā)的新方向。
根據(jù)Nielsen公司2013年6月的調(diào)查,智能機(jī)中53%為Android操作系統(tǒng),領(lǐng)先于Apple IOS、BlackBerry以及Windows Phone等其他手機(jī)操作系統(tǒng)。作為程序開發(fā)人員,掌握Android平臺下手機(jī)軟件的開發(fā)技術(shù)勢在必行。在Android開發(fā)中,掌握Android消息的處理方法,是從事Android開發(fā)的必備條件。
一個Android應(yīng)用中,在不同時機(jī)都會有大量的消息生成和傳遞,如圖1所示。①父Activity界面可以向子Activity界面?zhèn)魉徒Y(jié)果,同樣子Activity也可以向父Activity傳遞用戶的選擇。②一個Activity中可以嵌入多個Fragment片斷,F(xiàn)ragment接收用戶的操作,并將操作結(jié)果在另一個Fragment中顯示時,同樣存在著數(shù)據(jù)的傳遞。③UI主線程在需要執(zhí)行耗時任務(wù)時,通常會啟動子線程完成工作,因此子線程與UI主線程之間必然需要信息的互通。
下文,將根據(jù)產(chǎn)生消息組件的類型不同,來說明組件間消息傳遞的方法。
圖1 Android應(yīng)用中消息的傳遞
一個Android應(yīng)用,通常會有多個界面組成,這些界面即為Activity。在一個界面中調(diào)用另一個界面,這兩個界面即存在父子關(guān)系。父界面要向子界面?zhèn)鬟f結(jié)果信息,或者子界面要向父界面反饋用戶選擇結(jié)果,這時在兩個Activity之間負(fù)責(zé)傳遞消息的中介為Intent對象,如圖2所示。
圖2 Activity消息傳遞
父Activity中通過startActivity(Intent)方法通知Android操作系統(tǒng)要調(diào)用子Activity,并在Intent對象中放入要傳遞的數(shù)據(jù)。子Activity通常在其on?Create()方法中,獲取Intent對象,并提取傳遞來的數(shù)據(jù)。
根據(jù)Android參考文檔(API level 19)中的解釋,Intent類是將要執(zhí)行動作的抽象描述,可以用其啟動Activity,或與Service交互,或是向Broadcast?Receiver組件發(fā)送消息。
Intent最重要的用途是啟動Activities,一個In?tent包含的重要信息有兩塊,即action和data。ac?tion是指動作的簡要描述,如系統(tǒng)定義的“VIEW_ACTION”為查看;data用Uri的形式表示,描述要action要操作的數(shù)據(jù),如“tel:123”,該Uri數(shù)據(jù)表示電話號碼123。
Intent除了action和data這兩個重要屬性外,還有一個extras屬性,可用于攜帶Bundle類型的額外數(shù)據(jù),我們在Activity間傳遞的數(shù)據(jù)就是通過調(diào)用Intent對象的putExtras(Bundle)方法,放入這個屬性中。下面的示例,將顯示啟動一個子Activity,并將當(dāng)前的時間作為消息進(jìn)行傳送。
若子Activity需要反饋用戶結(jié)果給父Activity,則需要按以下步驟進(jìn)行:
1)父 Activity 以 StartActivityForResult(Intent,int requestCode)的方法啟動子Activity。
2)子Activity在合適時機(jī),通過setResult(int re?sultCode,Intent),設(shè)置結(jié)果狀態(tài),返回結(jié)果。
3)父 Activity覆寫 protected void onActivityRe?sult(int requestCode,int resultCode,Intent data)方法,對子Activity返回結(jié)果進(jìn)行處理。
父Activity可以啟動多個子Activity,并要求這些子界面返回用戶操作結(jié)果,不同子Activity以用戶自定義的requestCode進(jìn)行區(qū)分。子Activity使用setResult()設(shè)置結(jié)果,這會激活父界面使用onAc?tivityResult()進(jìn)行事件處理,父界面在使用request?Code區(qū)分子Activity后,就可以用同樣的方法處理Intent中的extras屬性攜帶的數(shù)據(jù)。
Fragment是Android3.0新增加的特征,是可以被嵌入在Activity中,響應(yīng)用戶操作的UI組件。一個Activity界面中可以被嵌入一個或多個Frag?ment。如多標(biāo)簽頁面,或者平板電腦中大用戶界面的分塊顯示都需要用到Fragment。當(dāng)在多個Frag?ment之間切換,并進(jìn)行數(shù)據(jù)交換時,可以直接調(diào)用Fragment對象上setArguments(Bundle)方法設(shè)置數(shù)據(jù),在目標(biāo)Fragment中使用getArguments()來獲取數(shù)據(jù)。
下面的例子假設(shè)Activity上有兩個Fragment。當(dāng)前顯示的是Fragment1,當(dāng)界面組件從Fragment1切換至Fragment2時,數(shù)據(jù)會需要從Fragment1傳送到Fragment2。
Activity中要進(jìn)行Fragment的各項(xiàng)操作,要先獲取FragmentTransaction對象,并使用它的add(),replace()或remove()方法對界面組件進(jìn)行添加,替換或刪除操作,并在最后使用FragmentTransaction對象的commit()方法讓Fragment操作執(zhí)行。從以上代碼可知,F(xiàn)ragment2即將要顯示在界面上。
Fragment要獲取其他片斷傳遞來的數(shù)據(jù),通常在onCreateView()方法中實(shí)現(xiàn)代碼,通過調(diào)用繼承的getArguments()方法就能獲得Bundle數(shù)據(jù)。
Android支持多線程編程,即一個應(yīng)用程序中可以創(chuàng)建多個線程,同時執(zhí)行多個任務(wù)。我們經(jīng)常會把耗時的工作放在子線程中執(zhí)行,避免UI界面延遲對用戶操作的響應(yīng),當(dāng)子線程執(zhí)行完畢后,可能有結(jié)果需要反饋給UI線程進(jìn)行界面的更新。當(dāng)然除了子線程需要返回數(shù)據(jù)給UI線程,多個子線程之間可能也需要數(shù)據(jù)的交換,線程間消息傳遞的橋梁,是名為Handler的類。
Handler類的主要作用有兩個,一個是向Han?dler類所在線程發(fā)送消息,另一個是幫助所在線程獲取和處理消息,如圖3所示。
圖3 Handler工作原理
為了更好地理解Handler工作原理,需要了解與Handler一起工作的三個組件,分別是Message,Looper以及MessageQueue。
1)Message:是Handler接收和處理的消息對象。
2)MessageQueue:是消息隊(duì)列,存放Message,并由Looper不斷進(jìn)行讀取。
3)Looper:Looper類有一個loop()方法,用一個死循環(huán)不斷取出MessageQueue中的消息,并將取出的消息分給該消息對應(yīng)的Handler進(jìn)行處理。每個線程都只能有一個Looper對象。
如果線程需要獲取其他線程的消息,就需要在線程中創(chuàng)建Looper對象以及Handler對象,Loop?er對象,需要使用Looper.prepare()方法創(chuàng)建,一旦Looper對象初始化成功,系統(tǒng)會同時創(chuàng)建為線程創(chuàng)建MessageQueue對象。唯一例外的線程是UI主線程,Android系統(tǒng)在UI主線程中已經(jīng)初始化了一個Looper對象,因此當(dāng)UI主線程需要獲取其他線程消息時,只要創(chuàng)建Handler對象即可。
下面的示例代碼,用于非UI線程接受其他子線程的消息。需要提醒的是Looper對象的創(chuàng)建不能放在子線程的構(gòu)造方法中,必須放在run()方法內(nèi)。
public class MyThread implements Runnable{
若其他線程向示例中的MyThread線程發(fā)送消息,則要調(diào)用MyThread對象.threadHandler.send?Message(Message)方法,因此子線程的Handler對象的訪問級別最好設(shè)置為public,使其他子線程方便調(diào)用。子線程中的Looper對象在MessageQueue發(fā)現(xiàn)新消息后,會通知Handler對象調(diào)用handleMes?sage()方法進(jìn)行處理。
在我們實(shí)際的手機(jī)應(yīng)用編程中,消息的處理非常廣泛,比如用戶配置界面向主界面返回配置數(shù)據(jù),游戲程序中游戲狀態(tài)需要周期性地更新UI,又如網(wǎng)絡(luò)數(shù)據(jù)的獲取反饋等。本文詳細(xì)介紹并分析了Activity界面、Fragment片斷以及線程Thread之間信息傳遞的原理和方法,對提升程序員的An?droid手機(jī)程序編制水平是非常有幫助的。
[1]紀(jì)曉陽.線程在Android開發(fā)中的應(yīng)用[J].軟件,2013,34(8):24-26.
[2]解志群.Android多線程與消息循環(huán)[J].電子世界,2013(19):87-88.
[3]李剛.瘋狂Android講義[M].北京:電子工業(yè)出版社,2013.
[4]楊豐盛.Android技術(shù)內(nèi)幕[M].北京:機(jī)械工業(yè)出版社,2011.