>應(yīng)用程序基礎(chǔ)關(guān)鍵類ActivityServiceBroadcastReceiverContentProviderIntent
Android應(yīng)用程序使用Java做為開(kāi)發(fā)語(yǔ)言。[size=1em]aapt工具把編譯后的Java代碼連同其它應(yīng)用程序需要的數(shù)據(jù)和資源文件一起打包到一個(gè)Android包文件中,這個(gè)文件使用.apk做為擴(kuò)展名,它是分發(fā)應(yīng)用程序并安裝到移動(dòng)設(shè)備的媒介,用戶只需下載并安裝此文件到他們的設(shè)備。單一.apk文件中的所有代碼被認(rèn)為是一個(gè)應(yīng)用程序。 從很多方面來(lái)看,每個(gè)Android應(yīng)用程序都存在于它自己的世界之中: 默認(rèn)情況下,每個(gè)應(yīng)用程序均運(yùn)行于它自己的Linux進(jìn)程中。當(dāng)應(yīng)用程序中的任意代碼開(kāi)始執(zhí)行時(shí),Android啟動(dòng)一個(gè)進(jìn)程,而當(dāng)不再需要此進(jìn)程而其它應(yīng)用程序又需要系統(tǒng)資源時(shí),則關(guān)閉這個(gè)進(jìn)程。每個(gè)進(jìn)程都運(yùn)行于自己的Java虛擬機(jī)(VM)中。所以應(yīng)用程序代碼實(shí)際上與其它應(yīng)用程序的代碼是隔絕的。默認(rèn)情況下,每個(gè)應(yīng)用程序均被賦予一個(gè)唯一的Linux用戶ID,并加以權(quán)限設(shè)置,使得應(yīng)用程序的文件僅對(duì)這個(gè)用戶、這個(gè)應(yīng)用程序可見(jiàn)。當(dāng)然,也有其它的方法使得這些文件同樣能為別的應(yīng)用程序所訪問(wèn)。
使兩個(gè)應(yīng)用程序共有同一個(gè)用戶ID是可行的,這種情況下他們可以看到彼此的文件。從系統(tǒng)資源維護(hù)的角度來(lái)看,擁有同一個(gè)ID的應(yīng)用程序也將在運(yùn)行時(shí)使用同一個(gè)Linux進(jìn)程,以及同一個(gè)虛擬機(jī)。 應(yīng)用程序組件Android的核心功能之一就是一個(gè)應(yīng)用程序可以使用其它應(yīng)用程序的元素(如果那個(gè)應(yīng)用程序允許的話)。比如說(shuō),如果你的應(yīng)用程序需要一個(gè)圖片卷動(dòng)列表,而另一個(gè)應(yīng)用程序已經(jīng)開(kāi)發(fā)了一個(gè)合用的而又允許別人使用的話,你可以直接調(diào)用那個(gè)卷動(dòng)列表來(lái)完成工作,而不用自己再開(kāi)發(fā)一個(gè)。你的應(yīng)用程序并沒(méi)有吸納或鏈接其它應(yīng)用程序的代碼,它只是在有需求的時(shí)候啟動(dòng)了其它應(yīng)用程序的那個(gè)功能部分。 為達(dá)到這個(gè)目的,系統(tǒng)必須在一個(gè)應(yīng)用程序的一部分被需要時(shí)啟動(dòng)這個(gè)應(yīng)用程序,并將那個(gè)部分的Java對(duì)象實(shí)例化。與在其它系統(tǒng)上的應(yīng)用程序不同,Android應(yīng)用程序沒(méi)有為應(yīng)用準(zhǔn)備一個(gè)單獨(dú)的程序入口(比如說(shuō),沒(méi)有[size=1em]main()方法), 而是為系統(tǒng)依照需求實(shí)例化提供了基本的組件。共有四種組件類型: Activity是為用戶操作而展示的可視化用戶界面。比如說(shuō),一個(gè)activity可以展示一個(gè)菜單項(xiàng)列表供用戶選擇,或者顯示一些包含說(shuō)明的照片。一個(gè)短消息應(yīng)用程序可以包括一個(gè)用于顯示做為發(fā)送對(duì)象的聯(lián)系人的列表的activity,一個(gè)給選定的聯(lián)系人寫短信的activity以及翻閱以前的短信和改變?cè)O(shè)置的activity。盡管它們一起組成了一個(gè)內(nèi)聚的用戶界面,但其中每個(gè)activity都與其它的保持獨(dú)立。每個(gè)都是以Activity類為基類的子類實(shí)現(xiàn)。 一個(gè)應(yīng)用程序可以只有一個(gè)activity,或者,如剛才提到的短信應(yīng)用程序那樣,包含很多個(gè)。每個(gè)activity的作用,以及其數(shù)目,自然取決于應(yīng)用程序及其設(shè)計(jì)。一般情況下,總有一個(gè)應(yīng)用程序被標(biāo)記為用戶在應(yīng)用程序啟動(dòng)的時(shí)候第一個(gè)看到的。從一個(gè)activity轉(zhuǎn)向另一個(gè)的方式是靠當(dāng)前的activity啟動(dòng)下一個(gè)。 每個(gè)activity都被給予一個(gè)默認(rèn)的窗口以進(jìn)行繪制。一般情況下,這個(gè)窗口是滿屏的,但它也可以是一個(gè)小的位于其它窗口之上的浮動(dòng)窗口。一個(gè)activity也可以使用超過(guò)一個(gè)的窗口──比如,在activity運(yùn)行過(guò)程中彈出的一個(gè)供用戶反應(yīng)的小對(duì)話框,或是當(dāng)用戶選擇了屏幕上特定項(xiàng)目后顯示的必要信息。 窗口顯示的可視內(nèi)容是由一系列視圖構(gòu)成的,這些視圖均繼承自 View 基類。每個(gè)視圖均控制著窗口中一塊特定的矩形空間。父級(jí)視圖包含并組織它子視圖的布局。葉節(jié)點(diǎn)視圖(位于視圖層次最底端)在它們控制的矩形中進(jìn)行繪制,并對(duì)用戶對(duì)其直接操作做出響應(yīng)。所以,視圖是activity與用戶進(jìn)行交互的界面。比如說(shuō),視圖可以顯示一個(gè)小圖片,并在用戶指點(diǎn)它的時(shí)候產(chǎn)生動(dòng)作。Android有很多既定的視圖供用戶直接使用,包括按鈕、文本域、卷軸、菜單項(xiàng)、復(fù)選框等等。 視圖層次是由Activity.setContentView() 方法放入activity的窗口之中的。上下文視圖是位于視圖層次根位置的視圖對(duì)象。(參見(jiàn)用戶界面章節(jié)獲取關(guān)于視圖及層次的更多信息。) 服務(wù)沒(méi)有可視化的用戶界面,而是在一段時(shí)間內(nèi)在后臺(tái)運(yùn)行。比如說(shuō),一個(gè)服務(wù)可以在用戶做其它事情的時(shí)候在后臺(tái)播放背景音樂(lè)、從網(wǎng)絡(luò)上獲取一些數(shù)據(jù)或者計(jì)算一些東西并提供給需要這個(gè)運(yùn)算結(jié)果的activity使用。每個(gè)服務(wù)都繼承自Service基類。 一個(gè)媒體播放器播放播放列表中的曲目是一個(gè)不錯(cuò)的例子。播放器應(yīng)用程序可能有一個(gè)或多個(gè)activity來(lái)給用戶選擇歌曲并進(jìn)行播放。然而,音樂(lè)播放這個(gè)任務(wù)本身不應(yīng)該為任何activity所處理,因?yàn)橛脩羝谕谒麄冸x開(kāi)播放器應(yīng)用程序而開(kāi)始做別的事情時(shí),音樂(lè)仍在繼續(xù)播放。為達(dá)到這個(gè)目的,媒體播放器activity應(yīng)該啟用一個(gè)運(yùn)行于后臺(tái)的服務(wù)。而系統(tǒng)將在這個(gè)activity不再顯示于屏幕之后,仍維持音樂(lè)播放服務(wù)的運(yùn)行。 你可以連接至(綁定)一個(gè)正在運(yùn)行的服務(wù)(如果服務(wù)沒(méi)有運(yùn)行,則啟動(dòng)之)。連接之后,你可以通過(guò)那個(gè)服務(wù)暴露出來(lái)的接口與服務(wù)進(jìn)行通訊。對(duì)于音樂(lè)服務(wù)來(lái)說(shuō),這個(gè)接口可以允許用戶暫停、回退、停止以及重新開(kāi)始播放。 如同activity和其它組件一樣,服務(wù)運(yùn)行于應(yīng)用程序進(jìn)程的主線程內(nèi)。所以它不會(huì)對(duì)其它組件或用戶界面有任何干擾,它們一般會(huì)派生一個(gè)新線程來(lái)進(jìn)行一些耗時(shí)任務(wù)(比如音樂(lè)回放)。參見(jiàn)下述 進(jìn)程和線程 。 廣播接收器是一個(gè)專注于接收廣播通知信息,并做出對(duì)應(yīng)處理的組件。很多廣播是源自于系統(tǒng)代碼的──比如,通知時(shí)區(qū)改變、電池電量低、拍攝了一張照片或者用戶改變了語(yǔ)言選項(xiàng)。應(yīng)用程序也可以進(jìn)行廣播──比如說(shuō),通知其它應(yīng)用程序一些數(shù)據(jù)下載完成并處于可用狀態(tài)。
應(yīng)用程序可以擁有任意數(shù)量的廣播接收器以對(duì)所有它感興趣的通知信息予以響應(yīng)。所有的接收器均繼承自BroadcastReceiver基類。
廣播接收器沒(méi)有用戶界面。然而,它們可以啟動(dòng)一個(gè)activity來(lái)響應(yīng)它們收到的信息,或者用NotificationManager來(lái)通知用戶。通知可以用很多種方式來(lái)吸引用戶的注意力──閃動(dòng)背燈、震動(dòng)、播放聲音等等。一般來(lái)說(shuō)是在狀態(tài)欄上放一個(gè)持久的圖標(biāo),用戶可以打開(kāi)它并獲取消息。 內(nèi)容提供者將一些特定的應(yīng)用程序數(shù)據(jù)供給其它應(yīng)用程序使用。數(shù)據(jù)可以存儲(chǔ)于文件系統(tǒng)、SQLite數(shù)據(jù)庫(kù)或其它方式。內(nèi)容提供者繼承于ContentProvider 基類,為其它應(yīng)用程序取用和存儲(chǔ)它管理的數(shù)據(jù)實(shí)現(xiàn)了一套標(biāo)準(zhǔn)方法。然而,應(yīng)用程序并不直接調(diào)用這些方法,而是使用一個(gè) ContentResolver 對(duì)象,調(diào)用它的方法作為替代。ContentResolver可以與任意內(nèi)容提供者進(jìn)行會(huì)話,與其合作來(lái)對(duì)所有相關(guān)交互通訊進(jìn)行管理。 參閱獨(dú)立的內(nèi)容提供者章節(jié)獲得更多關(guān)于使用內(nèi)容提供者的內(nèi)容。 每當(dāng)出現(xiàn)一個(gè)需要被特定組件處理的請(qǐng)求時(shí),Android會(huì)確保那個(gè)組件的應(yīng)用程序進(jìn)程處于運(yùn)行狀態(tài),或在必要的時(shí)候啟動(dòng)它。并確保那個(gè)相應(yīng)組件的實(shí)例的存在,必要時(shí)會(huì)創(chuàng)建那個(gè)實(shí)例。 激活組件:intent當(dāng)接收到ContentResolver發(fā)出的請(qǐng)求后,內(nèi)容提供者被激活。而其它三種組件──activity、服務(wù)和廣播接收器被一種叫做intent的異步消息所激活。intent是一個(gè)保存著消息內(nèi)容的Intent對(duì)象。對(duì)于activity和服務(wù)來(lái)說(shuō),它指明了請(qǐng)求的操作名稱以及作為操作對(duì)象的數(shù)據(jù)的URI和其它一些信息。比如說(shuō),它可以承載對(duì)一個(gè)activity的請(qǐng)求,讓它為用戶顯示一張圖片,或者讓用戶編輯一些文本。而對(duì)于廣播接收器而言,Intent對(duì)象指明了聲明的行為。比如,它可以對(duì)所有感興趣的對(duì)象聲明照相按鈕被按下。 對(duì)于每種組件來(lái)說(shuō),激活的方法是不同的: 通過(guò)傳遞一個(gè)Intent對(duì)象至 Context.startActivity()或Activity.startActivityForResult()以載入(或指定新工作給)一個(gè)activity。相應(yīng)的activity可以通過(guò)調(diào)用 getIntent() 方法來(lái)查看激活它的intent。Android通過(guò)調(diào)用activity的onNewIntent()方法來(lái)傳遞給它繼發(fā)的intent。
一個(gè)activity經(jīng)常啟動(dòng)了下一個(gè)。如果它期望它所啟動(dòng)的那個(gè)activity返回一個(gè)結(jié)果,它會(huì)以調(diào)用[size=1em]startActivityForResult()來(lái)取代startActivity()。比如說(shuō),如果它啟動(dòng)了另外一個(gè)activity以使用戶挑選一張照片,它也許想知道哪張照片被選中了。結(jié)果將會(huì)被封裝在一個(gè)Intent對(duì)象中,并傳遞給發(fā)出調(diào)用的activity的onActivityResult() 方法。通過(guò)傳遞一個(gè)Intent對(duì)象至Context.startService()將啟動(dòng)一個(gè)服務(wù)(或給予正在運(yùn)行的服務(wù)以一個(gè)新的指令)。Android調(diào)用服務(wù)的 onStart()方法并將Intent對(duì)象傳遞給它。
與此類似,一個(gè)Intent可以被調(diào)用組件傳遞給 Context.bindService()以獲取一個(gè)正在運(yùn)行的目標(biāo)服務(wù)的連接。這個(gè)服務(wù)會(huì)經(jīng)由onBind() 方法的調(diào)用獲取這個(gè)Intent對(duì)象(如果服務(wù)尚未啟動(dòng),[size=1em]bindService()會(huì)先啟動(dòng)它)。比如說(shuō),一個(gè)activity可以連接至前述的音樂(lè)回放服務(wù),并提供給用戶一個(gè)可操作的(用戶界面)以對(duì)回放進(jìn)行控制。這個(gè)activity可以調(diào)用 [size=1em]bindService() 來(lái)建立連接,然后調(diào)用服務(wù)中定義的對(duì)象來(lái)影響回放。
后面一節(jié):遠(yuǎn)程方法調(diào)用將更詳細(xì)的闡明如何綁定至服務(wù)。應(yīng)用程序可以憑借將Intent對(duì)象傳遞給 Context.sendBroadcast() ,Context.sendOrderedBroadcast(), 以及Context.sendStickyBroadcast()和其它類似方法來(lái)產(chǎn)生一個(gè)廣播。Android會(huì)調(diào)用所有對(duì)此廣播有興趣的廣播接收器的 onReceive()方法,將intent傳遞給它們。
欲了解更多intent消息的信息,請(qǐng)參閱獨(dú)立章節(jié) Intent和Intent濾過(guò)器。 關(guān)閉組件內(nèi)容提供者僅在響應(yīng)ContentResolver提出請(qǐng)求的時(shí)候激活。而一個(gè)廣播接收器僅在響應(yīng)廣播信息的時(shí)候激活。所以,沒(méi)有必要去顯式的關(guān)閉這些組件。 而activity則不同,它提供了用戶界面,并與用戶進(jìn)行會(huì)話。所以只要會(huì)話依然持續(xù),哪怕對(duì)話過(guò)程暫時(shí)停頓,它都會(huì)一直保持激活狀態(tài)。與此相似,服務(wù)也會(huì)在很長(zhǎng)一段時(shí)間內(nèi)保持運(yùn)行。所以Android為關(guān)閉activity和服務(wù)提供了一系列的方法。 可以通過(guò)調(diào)用它的finish()方法來(lái)關(guān)閉一個(gè)activity。一個(gè)activity可以通過(guò)調(diào)用另外一個(gè)activity(它用[size=1em]startActivityForResult() 啟動(dòng)的)的finishActivity()方法來(lái)關(guān)閉它。服務(wù)可以通過(guò)調(diào)用它的stopSelf()方法來(lái)停止,或者調(diào)用 Context.stopService()。
系統(tǒng)也會(huì)在組件不再被使用的時(shí)候或者Android需要為 活動(dòng)組件聲明更多內(nèi)存的時(shí)候關(guān)閉它。后面的 組件的生命周期一節(jié),將對(duì)這種可能及附屬情況進(jìn)行更詳細(xì)的討論。 manifest文件當(dāng)Android啟動(dòng)一個(gè)應(yīng)用程序組件之前,它必須知道那個(gè)組件是存在的。所以,應(yīng)用程序會(huì)在一個(gè)manifest文件中聲明它的組件,這個(gè)文件會(huì)被打包到Android包中。這個(gè).apk文件還將涵括應(yīng)用程序的代碼、文件以及其它資源。 這個(gè)manifest文件以XML作為結(jié)構(gòu)格式,而且對(duì)于所有應(yīng)用程序,都叫做AndroidManifest.xml。為聲明一個(gè)應(yīng)用程序組件,它還會(huì)做很多額外工作,比如指明應(yīng)用程序所需鏈接到的庫(kù)的名稱(除了默認(rèn)的Android庫(kù)之外)以及聲明應(yīng)用程序期望獲得的各種權(quán)限。 但manifest文件的主要功能仍然是向Android聲明應(yīng)用程序的組件。舉例說(shuō)明,一個(gè)activity可以如下聲明: - <?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
<application . . . >
<activity android:name="com.example.project.FreneticActivity"
android:icon="@drawable/small_pic.png"
android:label="@string/freneticLabel"
. . . >
</activity>
. . .
</application>
</manifest>
復(fù)制代碼 <activity>元素的name屬性指定了實(shí)現(xiàn)了這個(gè)activity的 Activity的子類。icon和label屬性指向了包含展示給用戶的此activity的圖標(biāo)和標(biāo)簽的資源文件。 其它組件也以類似的方法聲明──<service> 元素用于聲明服務(wù), <receiver> 元素用于聲明廣播接收器,而 <provider> 元素用于聲明內(nèi)容提供者。 manifest文件中未進(jìn)行聲明的activity、服務(wù)以及內(nèi)容提供者將不為系統(tǒng)所見(jiàn),從而也就不會(huì)被運(yùn)行。然而,廣播接收器既可以在manifest文件中聲明,也可以在代碼中進(jìn)行動(dòng)態(tài)的創(chuàng)建,并以調(diào)用Context.registerReceiver()的方式注冊(cè)至系統(tǒng)。 欲更多了解如何為你的應(yīng)用程序構(gòu)建manifest文件,請(qǐng)參閱AndroidManifest.xml文件一章。 Intent過(guò)濾器Intent對(duì)象可以被顯式的指定目標(biāo)組件。如果進(jìn)行了這種指定,Android會(huì)找到這個(gè)組件(依據(jù)manifest文件中的聲明)并激活它。但如果Intent沒(méi)有進(jìn)行顯式的指定,Android就必須為它找到對(duì)于intent來(lái)說(shuō)最合適的組件。這個(gè)過(guò)程是通過(guò)比較Intent對(duì)象和所有可能對(duì)象的intent過(guò)濾器完成的。組件的intent過(guò)濾器會(huì)告知Android它所能處理的intent類型。如同其它相對(duì)于組件很重要的信息一樣,這些是在manifest文件中進(jìn)行聲明的。這里是上面實(shí)例的一個(gè)擴(kuò)展,其中加入了針對(duì)activity的兩個(gè)intent過(guò)濾器聲明: - <?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
<application . . . >
<activity android:name="com.example.project.FreneticActivity"
android:icon="@drawable/small_pic.png"
android:label="@string/freneticLabel"
. . . >
<intent-filter . . . >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter . . . >
<action android:name="com.example.project.BOUNCE" />
<data android:type="image/jpeg" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
. . .
</application>
</manifest>
復(fù)制代碼 示例中的第一個(gè)過(guò)濾器──action “[size=1em]android.intent.action.MAIN”和類別“[size=1em]android.intent.category.LAUNCHER”的組合──是通常具有的。它標(biāo)明了這個(gè)activity將在應(yīng)用程序加載器中顯示,就是用戶在設(shè)備上看到的可供加載的應(yīng)用程序列表。換句話說(shuō),這個(gè)activity是應(yīng)用程序的入口,是用戶選擇運(yùn)行這個(gè)應(yīng)用程序后所見(jiàn)到的第一個(gè)activity。 第二個(gè)過(guò)濾器聲明了這個(gè)activity能被賦予一種特定類型的數(shù)據(jù)。 組件可以擁有任意數(shù)量的intent過(guò)濾器,每個(gè)都會(huì)聲明一系列不同的能力。如果它沒(méi)有包含任何過(guò)濾器,它將只能被顯式聲明了目標(biāo)組件名稱的intent激活。 對(duì)于在代碼中創(chuàng)建并注冊(cè)的廣播接收器來(lái)說(shuō),intent過(guò)濾器將被直接以 IntentFilter對(duì)象實(shí)例化。其它過(guò)濾器則在manifest文件中設(shè)置。 欲獲得更多intent過(guò)濾器的信息,請(qǐng)參閱獨(dú)立章節(jié): Intent和Intent過(guò)濾器。 Activity和任務(wù)如前所述,一個(gè)activity可以啟動(dòng)另外一個(gè),甚至包括與它不處于同一應(yīng)用程序之中的。舉個(gè)例子說(shuō),假設(shè)你想讓用戶看到某個(gè)地方的街道地圖。而已經(jīng)存在一個(gè)具有此功能的activity了,那么你的activity所需要做的工作就是把請(qǐng)求信息放到一個(gè)Intent對(duì)象里面,并把它傳遞給[size=1em]startActivity()。于是地圖瀏覽器就會(huì)顯示那個(gè)地圖。而當(dāng)用戶按下BACK鍵的時(shí)候,你的activity又會(huì)再一次的顯示在屏幕上。 對(duì)于用戶來(lái)說(shuō),這看起來(lái)就像是地圖瀏覽器是你activity所在的應(yīng)用程序中的一個(gè)組成部分,其實(shí)它是在另外一個(gè)應(yīng)用程序中定義,并運(yùn)行在那個(gè)應(yīng)用程序的進(jìn)程之中的。Android將這兩個(gè)activity放在同一個(gè)任務(wù)中來(lái)維持一個(gè)完整的用戶體驗(yàn)。簡(jiǎn)單的說(shuō),任務(wù)就是用戶所體驗(yàn)到的“應(yīng)用程序”。它是安排在一個(gè)堆棧中的一組相關(guān)的activity。堆棧中的根activity就是啟動(dòng)了這整個(gè)任務(wù)的那個(gè)──一般情況下,它就是用戶在應(yīng)用程序加載器中所選擇的。而堆棧最上方的activity則是當(dāng)前運(yùn)行的──用戶直接對(duì)其進(jìn)行操作的。當(dāng)一個(gè)activity啟動(dòng)另外一個(gè)的時(shí)候,新的activity就被壓入堆棧,并成為當(dāng)前運(yùn)行的activity。而前一個(gè)activity仍保持在堆棧之中。當(dāng)用戶按下BACK鍵的時(shí)候,當(dāng)前activity出棧,而前一個(gè)恢復(fù)為當(dāng)前運(yùn)行的activity。 堆棧中保存的其實(shí)是對(duì)象,所以如果發(fā)生了諸如需要多個(gè)地圖瀏覽器的情況,就會(huì)使得一個(gè)任務(wù)中出現(xiàn)多個(gè)同一Activity子類的實(shí)例同時(shí)存在,堆棧會(huì)為每個(gè)實(shí)例單獨(dú)開(kāi)辟一個(gè)入口。堆棧中的Activity永遠(yuǎn)不會(huì)重排,只會(huì)壓入或彈出。 任務(wù)其實(shí)就是activity的堆棧,而不是manifest文件中的一個(gè)類或者元素。所以你無(wú)法撇開(kāi)activity而為一個(gè)任務(wù)設(shè)置一個(gè)值。而事實(shí)上整個(gè)任務(wù)使用的值是在根activity中設(shè)置的。比如說(shuō),下一節(jié)我們會(huì)談及“任務(wù)的affinity”,從affinity中讀出的值將會(huì)設(shè)置到任務(wù)的根activity之中。 任務(wù)中的所有activity是作為一個(gè)整體進(jìn)行移動(dòng)的。整個(gè)的任務(wù)(即activity堆棧)可以移到前臺(tái),或退至后臺(tái)。舉個(gè)例子說(shuō),比如當(dāng)前任務(wù)在堆棧中存有四個(gè)activity──三個(gè)在當(dāng)前activity之下。當(dāng)用戶按下HOME鍵的時(shí)候,回到了應(yīng)用程序加載器,然后選擇了一個(gè)新的應(yīng)用程序(也就是一個(gè)新任務(wù))。則當(dāng)前任務(wù)遁入后臺(tái),而新任務(wù)的根activity顯示出來(lái)。然后,過(guò)了一小會(huì)兒,用戶再次回到了應(yīng)用程序加載器而又選擇了前一個(gè)應(yīng)用程序(上一個(gè)任務(wù))。于是那個(gè)任務(wù),帶著它堆棧中所有的四個(gè)activity,再一次的到了前臺(tái)。當(dāng)用戶按下BACK鍵的時(shí)候,屏幕不會(huì)顯示出用戶剛才離開(kāi)的activity(上一個(gè)任務(wù)的根activity)。取而代之,當(dāng)前任務(wù)的堆棧中最上面的activity被彈出,而同一任務(wù)中的上一個(gè)activity顯示了出來(lái)。 上述的種種即是activity和任務(wù)的默認(rèn)行為模式。但是有一些方法可以改變所有這一切。activity和任務(wù)的聯(lián)系、任務(wù)中activity的行為方式都被啟動(dòng)那個(gè)activity的Intent對(duì)象中設(shè)置的一系列標(biāo)記和manifest文件中那個(gè)activity中的<activity>元素的系列屬性之間的交互所控制。無(wú)論是請(qǐng)求發(fā)出者和回應(yīng)者在這里都擁有話語(yǔ)權(quán)。 我們剛才所說(shuō)的這些關(guān)鍵Intent標(biāo)記如下: [size=1em]FLAG_ACTIVITY_NEW_TASK
[size=1em]FLAG_ACTIVITY_CLEAR_TOP
[size=1em]FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
[size=1em]FLAG_ACTIVITY_SINGLE_TOP 而關(guān)鍵的[size=1em]<activity>屬性是: [size=1em]taskAffinity
[size=1em]launchMode
[size=1em]allowTaskReparenting
[size=1em]clearTaskOnLaunch
[size=1em]alwaysRetainTaskState
[size=1em]finishOnTaskLaunch 接下來(lái)的一節(jié)會(huì)描述這些標(biāo)記以及屬性的作用,它們是如何互相影響的,以及控制它們的使用時(shí)必須考慮到的因素。 Affinity(吸引力)和新任務(wù)默認(rèn)情況下,一個(gè)應(yīng)用程序中的activity相互之間會(huì)有一種Affinity──也就是說(shuō),它們首選都?xì)w屬于一個(gè)任務(wù)。然而,可以在[size=1em]<activity>元素中把每個(gè)activity的taskAffinity屬性設(shè)置為一個(gè)獨(dú)立的affinity。于是在不同的應(yīng)用程序中定義的activity可以享有同一個(gè)affinity,或者在同一個(gè)應(yīng)用程序中定義的activity有著不同的affinity。affinity在兩種情況下生效:當(dāng)加載activity的Intent對(duì)象包含了[size=1em]FLAG_ACTIVITY_NEW_TASK 標(biāo)記,或者當(dāng)activity的[size=1em]allowTaskReparenting屬性設(shè)置為“true”。 FLAG_ACTIVITY_NEW_TASK標(biāo)記 如前所述,在默認(rèn)情況下,一個(gè)新activity被另外一個(gè)調(diào)用了[size=1em]startActivity()方法的activity載入了任務(wù)之中。并壓入了調(diào)用者所在的堆棧。然而,如果傳遞給[size=1em]startActivity()的Intent對(duì)象包含了[size=1em]FLAG_ACTIVITY_NEW_TASK標(biāo)記,系統(tǒng)會(huì)為新activity安排另外一個(gè)任務(wù)。一般情況下,如同標(biāo)記所暗示的那樣,這會(huì)是一個(gè)新任務(wù)。然而,這并不是必然的。如果已經(jīng)存在了一個(gè)與新activity有著同樣affinity的任務(wù),則activity會(huì)載入那個(gè)任務(wù)之中。如果沒(méi)有,則啟用新任務(wù)。 allowTaskReparenting 屬性 如果一個(gè)activity將[size=1em]allowTaskReparenting屬性設(shè)置為“true”。它就可以從初始的任務(wù)中轉(zhuǎn)移到與其擁有同一個(gè)affinity并轉(zhuǎn)向前臺(tái)的任務(wù)之中。比如說(shuō),一個(gè)旅行應(yīng)用程序中包含的預(yù)報(bào)所選城市的天氣情況的activity。它與這個(gè)應(yīng)用程序中其它的activity擁有同樣的affinity(默認(rèn)的affinity)而且允許重定父級(jí)。你的另一個(gè)activity啟動(dòng)了天氣預(yù)報(bào),于是它就會(huì)與這個(gè)activity共處與同一任務(wù)之中。然而,當(dāng)那個(gè)旅行應(yīng)用程序再次回到前臺(tái)的時(shí)候,這個(gè)天氣預(yù)報(bào)activity就會(huì)被再次安排到原先的任務(wù)之中并顯示出來(lái)。 如果在用戶的角度看來(lái),一個(gè).apk文件中包含了多于一個(gè)的“應(yīng)用程序”,你可能會(huì)想要為它們所轄的activity安排不一樣的affinity。 加載模式[size=1em]<activity>元素的launchMode屬性可以設(shè)置四種不同的加載模式: "[size=1em]standard" (默認(rèn)值)
"[size=1em]singleTop"
"[size=1em]singleTask"
"[size=1em]singleInstance" 這些模式之間的差異主要體現(xiàn)在四個(gè)方面: 對(duì)“[size=1em]standard”和“[size=1em]singleTop”模式而言,是產(chǎn)生intent(并調(diào)用 startActivity())的任務(wù)──除非Intent對(duì)象包含F(xiàn)LAG_ACTIVITY_NEW_TASK標(biāo)記。而在這種情況下,如同上面Affinitie和新任務(wù)一節(jié)所述,會(huì)是另外一個(gè)任務(wù)。相反,對(duì)“[size=1em]singleTask”和“[size=1em]singleInstance”模式而言,activity總是位于任務(wù)的根部。正是它們定義了一個(gè)任務(wù),所以它們絕不會(huì)被載入到其它任務(wù)之中。 一個(gè)“[size=1em]standard”或“[size=1em]singleTop”的activity可以被多次初始化。它們可以歸屬于多個(gè)任務(wù),而一個(gè)任務(wù)也可以擁有同一activity的多個(gè)實(shí)例。 相反,對(duì)“[size=1em]singleTask”和“[size=1em]singleInstance”的activity被限定于只能有一個(gè)實(shí)例。因?yàn)檫@些activity都是任務(wù)的起源,這種限制意味著在一個(gè)設(shè)備中同一時(shí)間只允許存在一個(gè)任務(wù)的實(shí)例。 一個(gè)“[size=1em]singleInstance”模式的activity將會(huì)是它所在的任務(wù)中唯一的activity。如果它啟動(dòng)了別的activity,那個(gè)activity將會(huì)依據(jù)它自己的加載模式加載到其它的任務(wù)中去──如同在intent中設(shè)置了[size=1em]FLAG_ACTIVITY_NEW_TASK 標(biāo)記一樣的效果。在其它方面,“[size=1em]singleInstance”模式的效果與“[size=1em]singleTask”是一樣的。 剩下的三種模式允許一個(gè)任務(wù)中出現(xiàn)多個(gè)activity?!癧size=1em]singleTask”模式的activity將是任務(wù)的根activity,但它可以啟動(dòng)別的activity并將它們置入所在的任務(wù)中?!癧size=1em]standard”和“[size=1em]singleTop”activity則可以在堆棧的任意位置出現(xiàn)。
對(duì)默認(rèn)的"[size=1em]standard"模式來(lái)說(shuō),對(duì)于每個(gè)新intent都會(huì)創(chuàng)建一個(gè)新的實(shí)例以進(jìn)行響應(yīng),每個(gè)實(shí)例僅處理一個(gè)intent?!癧size=1em]singleTop”模式下,如果activity位于目的任務(wù)堆棧的最上面,則重用目前現(xiàn)存的activity來(lái)處理新的intent。如果它不是在堆棧頂部,則不會(huì)發(fā)生重用。而是創(chuàng)建一個(gè)新實(shí)例來(lái)處理新的intent并將其推入堆棧。舉例來(lái)說(shuō),假設(shè)一個(gè)任務(wù)的堆棧由根activityA和activity B、C和位于堆棧頂部的D組成,即堆棧A-B-C-D。一個(gè)針對(duì)D類型的activity的intent抵達(dá)的時(shí)候,如果D是默認(rèn)的“[size=1em]standard”加載模式,則創(chuàng)建并加載一個(gè)新的類實(shí)例,于是堆棧變?yōu)锳-B-C-D-D。 然而,如果D的載入模式為“[size=1em]singleTop”,則現(xiàn)有的實(shí)例會(huì)對(duì)新intent進(jìn)行處理(因?yàn)樗挥诙褩m敳浚┒褩13諥-B-C-D的形態(tài)。 換言之,如果新抵達(dá)的intent是針對(duì)B類型的activity,則無(wú)論B的模式是“[size=1em]standard”還是“[size=1em]singleTop” ,都會(huì)加載一個(gè)新的B的實(shí)例(因?yàn)锽不位于堆棧的頂部),而堆棧的順序變?yōu)锳-B-C-D-B。 如前所述,“[size=1em]singleTask”或“[size=1em]singleInstance”模式的activity永遠(yuǎn)不會(huì)存在多于一個(gè)實(shí)例。所以實(shí)例將處理所有新的intent。一個(gè)“[size=1em]singleInstance”模式的activity永遠(yuǎn)保持在堆棧的頂部(因?yàn)樗悄莻€(gè)堆棧中唯一的一個(gè)activity),所以它一直堅(jiān)守在處理intent的崗位上。然而,對(duì)一個(gè)“[size=1em]singleTask”模式的activity來(lái)說(shuō),它上面可能有,也可能沒(méi)有別的activity和它處于同一堆棧。[size=1em]在有的情況下,它就不在能夠處理intent的位置上,則那個(gè)intent將被舍棄。(即便在intent被舍棄的情況下,它的抵達(dá)仍將使這個(gè)任務(wù)切換至前臺(tái),并一直保留)
當(dāng)一個(gè)現(xiàn)存的activity被要求處理一個(gè)新的intent的時(shí)候,會(huì)調(diào)用onNewIntent()方法來(lái)將intent對(duì)象傳遞至activity。(啟動(dòng)activity的原始intent對(duì)象可以通過(guò)調(diào)用getIntent()方法獲得。) 請(qǐng)注意,當(dāng)一個(gè)新的activity實(shí)例被創(chuàng)建以處理新的intent的時(shí)候,用戶總可以按下BACK鍵來(lái)回到前面的狀態(tài)(回到前一個(gè)activity)。但當(dāng)使用現(xiàn)存的activity來(lái)處理新intent的時(shí)候,用戶是不能靠按下BACK鍵回到當(dāng)這個(gè)新intent抵達(dá)之前的狀態(tài)的。 想獲得更多關(guān)于加載模式的內(nèi)容,請(qǐng)參閱 <activity> 元素的描述。 清理堆棧如果用戶離開(kāi)一個(gè)任務(wù)很長(zhǎng)一段時(shí)間,系統(tǒng)會(huì)清理該任務(wù)中除了根activity之外的所有activity。當(dāng)用戶再次回到這個(gè)任務(wù)的時(shí)候,除了只剩下初始化activity尚存之外,其余都跟用戶上次離開(kāi)它的時(shí)候一樣。這樣做的原因是:在一段時(shí)間之后,用戶再次回到一個(gè)任務(wù)的時(shí)候,他們更期望放棄他們之前的所作所為,做些新的事情。 這些屬于默認(rèn)行為,另外,也存在一些activity的屬性用以控制并改變這些行為: alwaysRetainTaskState 屬性 如果一個(gè)任務(wù)的根activity中此屬性設(shè)置為“[size=1em]true”,則上述默認(rèn)行為不會(huì)發(fā)生。任務(wù)將在很長(zhǎng)的一段時(shí)間內(nèi)保留它堆棧內(nèi)的所有activity。 clearTaskOnLaunch屬性 如果一個(gè)任務(wù)的根activity中此屬性設(shè)置為“[size=1em]true”,則每當(dāng)用戶離開(kāi)這個(gè)任務(wù)和返回它的時(shí)候,堆棧都會(huì)被清空至只留下rootactivity。換句話說(shuō),這是[size=1em]alwaysRetainTaskState的另一個(gè)極端。哪怕僅是過(guò)了一小會(huì)兒,用戶回到任務(wù)時(shí),也是見(jiàn)到它的初始狀態(tài)。 finishOnTaskLaunch屬性 這個(gè)屬性與[size=1em]clearTaskOnLaunch屬性相似,但它僅作用于單個(gè)的activity,而不是整個(gè)的task。而且它可以使任意activity都被清理,甚至根activity也不例外。當(dāng)它設(shè)置為“[size=1em]true”的時(shí)候,此activity僅做為任務(wù)的一部分存在于當(dāng)前回話中,一旦用戶離開(kāi)并再次回到這個(gè)任務(wù),此activity將不復(fù)存在。 此外,還有別的方式從堆棧中移除一個(gè)activity。如果一個(gè)intent對(duì)象包含F(xiàn)LAG_ACTIVITY_CLEAR_TOP標(biāo)記,而且目標(biāo)任務(wù)的堆棧中已經(jīng)存在了一個(gè)能夠響應(yīng)此intent的activity類型的實(shí)例。則這個(gè)實(shí)例之上的所有activity都將被清理以使它位于堆棧的頂部來(lái)對(duì)intent做出響應(yīng)。如果此時(shí)指定的activity的加載模式為“[size=1em]standard”,則它本身也會(huì)從堆棧中移除,并加載一個(gè)新的實(shí)例來(lái)處理到來(lái)的intent。這是因?yàn)榧虞d模式為“[size=1em]standard”的activity總會(huì)創(chuàng)建一個(gè)新實(shí)例來(lái)處理新的intent。
|