Android開發指南中文版3
Activity和任務
如前所述,一個activity可以啟動另外一個,甚至包括與它不處於同一應用程式之中的。舉個例子說,假設你想讓使用者看到某個地方的街道地圖。而已經存在一個具有此功能的activity了,那麼你的activity所需要做的工作就是把請求資訊放到一個Intent物件裡面,並把它傳遞給startActivity()。於是地圖瀏覽器就會顯示那個地圖。而當用戶按下BACK鍵的時候,你的activity又會再一次的顯示在螢幕上。
對於使用者來說,這看起來就像是地圖瀏覽器是你activity所在的應用程式中的一個組成部分,其實它是在另外一個應用程式中定義,並執行在那個應用程式的程序之中的。
堆疊中儲存的其實是物件,所以如果發生了諸如需要多個地圖瀏覽器的情況,就會使得一個任務中出現多個同一Activity子類的例項同時存在,堆疊會為每個例項單獨開闢一個入口。堆疊中的Activity永遠不會重排,只會壓入或彈出。
任務其實就是activity的堆疊,而不是manifest檔案中的一個類或者元素。所以你無法撇開activity而為一個任務設定一個值。而事實上整個任務使用的值是在根activity中設定的。比如說,下一節我們會談及“任務的affinity”,從affinity中讀出的值將會設定到任務的根activity之中。
任務中的所有activity是作為一個整體進行移動的。整個的任務(即
上述的種種即是activity和任務的預設行為模式。但是有一些方法可以改變所有這一切。activity和任務的聯絡、任務中activity的行為方式都被啟動那個activity的Intent物件中設定的一系列標記和manifest檔案中那個activity中的元素的系列屬性之間的互動所控制。無論是請求發出者和迴應者在這裡都擁有話語權。
我們剛才所說的這些關鍵Intent標記如下:
FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_RESET_TASK_IF_NEEDEDFLAG_ACTIVITY_SINGLE_TOP
而關鍵的<activity>屬性是:
taskAffinitylaunchModeallowTaskReparentingclearTaskOnLaunchalwaysRetainTaskStatefinishOnTaskLaunch
接下來的一節會描述這些標記以及屬性的作用,它們是如何互相影響的,以及控制它們的使用時必須考慮到的因素。
Affinity(吸引力)和新任務
預設情況下,一個應用程式中的activity相互之間會有一種Affinity──也就是說,它們首選都歸屬於一個任務。然而,可以在<activity>元素中把每個activity的taskAffinity屬性設定為一個獨立的affinity。於是在不同的應用程式中定義的activity可以享有同一個affinity,或者在同一個應用程式中定義的activity有著不同的affinity。affinity在兩種情況下生效:當載入activity的Intent物件包含了FLAG_ACTIVITY_NEW_TASK標記,或者當activity的allowTaskReparenting屬性設定為“true”。
如前所述,在預設情況下,一個新activity被另外一個呼叫了startActivity()方法的activity載入了任務之中。並壓入了呼叫者所在的堆疊。然而,如果傳遞給startActivity()的Intent物件包含了FLAG_ACTIVITY_NEW_TASK標記,系統會為新activity安排另外一個任務。一般情況下,如同標記所暗示的那樣,這會是一個新任務。然而,這並不是必然的。如果已經存在了一個與新activity有著同樣affinity的任務,則activity會載入那個任務之中。如果沒有,則啟用新任務。
如果一個activity將allowTaskReparenting屬性設定為“true”。它就可以從初始的任務中轉移到與其擁有同一個affinity並轉向前臺的任務之中。比如說,一個旅行應用程式中包含的預報所選城市的天氣情況的activity。它與這個應用程式中其它的activity擁有同樣的affinity(預設的affinity)而且允許重定父級。你的另一個activity啟動了天氣預報,於是它就會與這個activity共處與同一任務之中。然而,當那個旅行應用程式再次回到前臺的時候,這個天氣預報activity就會被再次安排到原先的任務之中並顯示出來。
如果在使用者的角度看來,一個.apk檔案中包含了多於一個的“應用程式”,你可能會想要為它們所轄的activity安排不一樣的affinity。
載入模式
<activity>元素的屬性可以設定四種不同的載入模式:
"standard" (預設值)
"singleTop"
"singleTask"
"singleInstance"
這些模式之間的差異主要體現在四個方面:
-
哪個任務會把持對intent做出響應的activity。對“standard”和“singleTop”模式而言,是產生intent(並呼叫 )的任務──除非Intent物件包含標記。而在這種情況下,如同上面和新任務一節所述,會是另外一個任務。
相反,對“singleTask”和“singleInstance”模式而言,activity總是位於任務的根部。正是它們定義了一個任務,所以它們絕不會被載入到其它任務之中。
-
activity是否可以存在多個例項。一個“standard”或“singleTop”的activity可以被多次初始化。它們可以歸屬於多個任務,而一個任務也可以擁有同一activity的多個例項。
相反,對“singleTask”和“singleInstance”的activity被限定於只能有一個例項。因為這些activity都是任務的起源,這種限制意味著在一個裝置中同一時間只允許存在一個任務的例項。
-
在例項所在的任務中是否會有別的activity。一個“singleInstance”模式的activity將會是它所在的任務中唯一的activity。如果它啟動了別的activity,那個activity將會依據它自己的載入模式載入到其它的任務中去──如同在intent中設定了FLAG_ACTIVITY_NEW_TASK標記一樣的效果。在其它方面,“singleInstance”模式的效果與“singleTask”是一樣的。
剩下的三種模式允許一個任務中出現多個activity。“singleTask”模式的activity將是任務的根activity,但它可以啟動別的activity並將它們置入所在的任務中。“standard”和“singleTop”activity則可以在堆疊的任意位置出現。
-
是否要載入新的類例項以處理新的intent。對預設的"standard"模式來說,對於每個新intent都會建立一個新的例項以進行響應,每個例項僅處理一個intent。“singleTop”模式下,如果activity位於目的任務堆疊的最上面,則重用目前現存的activity來處理新的intent。如果它不是在堆疊頂部,則不會發生重用。而是建立一個新例項來處理新的intent並將其推入堆疊。
舉例來說,假設一個任務的堆疊由根activityA和activity B、C和位於堆疊頂部的D組成,即堆疊A-B-C-D。一個針對D型別的activity的intent抵達的時候,如果D是預設的“standard”載入模式,則建立並載入一個新的類例項,於是堆疊變為A-B-C-D-D。 然而,如果D的載入模式為“singleTop”,則現有的例項會對新intent進行處理(因為它位於堆疊頂部)而堆疊保持A-B-C-D的形態。
換言之,如果新抵達的intent是針對B型別的activity,則無論B的模式是“standard”還是“singleTop” ,都會載入一個新的B的例項(因為B不位於堆疊的頂部),而堆疊的順序變為A-B-C-D-B。
如前所述,“singleTask”或“singleInstance”模式的activity永遠不會存在多於一個例項。所以例項將處理所有新的intent。一個“singleInstance”模式的activity永遠保持在堆疊的頂部(因為它是那個堆疊中唯一的一個activity),所以它一直堅守在處理intent的崗位上。然而,對一個“singleTask”模式的activity來說,它上面可能有,也可能沒有別的activity和它處於同一堆疊。在有的情況下,它就不在能夠處理intent的位置上,則那個intent將被捨棄。(即便在intent被捨棄的情況下,它的抵達仍將使這個任務切換至前臺,並一直保留)
當一個現存的activity被要求處理一個新的intent的時候,會呼叫方法來將intent物件傳遞至activity。(啟動activity的原始intent物件可以通過呼叫方法獲得。)
請注意,當一個新的activity例項被建立以處理新的intent的時候,使用者總可以按下BACK鍵來回到前面的狀態(回到前一個activity)。但當使用現存的activity來處理新intent的時候,使用者是不能靠按下BACK鍵回到當這個新intent抵達之前的狀態的。
想獲得更多關於載入模式的內容,請參閱 元素的描述。
清理堆疊
如果使用者離開一個任務很長一段時間,系統會清理該任務中除了根activity之外的所有activity。當用戶再次回到這個任務的時候,除了只剩下初始化activity尚存之外,其餘都跟使用者上次離開它的時候一樣。這樣做的原因是:在一段時間之後,使用者再次回到一個任務的時候,他們更期望放棄他們之前的所作所為,做些新的事情。
這些屬於預設行為,另外,也存在一些activity的屬性用以控制並改變這些行為:
如果一個任務的根activity中此屬性設定為“true”,則上述預設行為不會發生。任務將在很長的一段時間內保留它堆疊內的所有activity。
如果一個任務的根activity中此屬性設定為“true”,則每當使用者離開這個任務和返回它的時候,堆疊都會被清空至只留下rootactivity。換句話說,這是alwaysRetainTaskState的另一個極端。哪怕僅是過了一小會兒,使用者回到任務時,也是見到它的初始狀態。
這個屬性與clearTaskOnLaunch屬性相似,但它僅作用於單個的activity,而不是整個的task。而且它可以使任意activity都被清理,甚至根activity也不例外。當它設定為“true”的時候,此activity僅做為任務的一部分存在於當前回話中,一旦使用者離開並再次回到這個任務,此activity將不復存在。
此外,還有別的方式從堆疊中移除一個activity。如果一個intent物件包含標記,而且目標任務的堆疊中已經存在了一個能夠響應此intent的activity型別的例項。則這個例項之上的所有activity都將被清理以使它位於堆疊的頂部來對intent做出響應。如果此時指定的activity的載入模式為“standard”,則它本身也會從堆疊中移除,並載入一個新的例項來處理到來的intent。這是因為載入模式為“standard”的activity總會建立一