1. 程式人生 > >Android開發指南中文版3

Android開發指南中文版3

Activity和任務

如前所述,一個activity可以啟動另外一個,甚至包括與它不處於同一應用程式之中的。舉個例子說,假設你想讓使用者看到某個地方的街道地圖。而已經存在一個具有此功能的activity了,那麼你的activity所需要做的工作就是把請求資訊放到一個Intent物件裡面,並把它傳遞給startActivity()。於是地圖瀏覽器就會顯示那個地圖。而當用戶按下BACK鍵的時候,你的activity又會再一次的顯示在螢幕上。

對於使用者來說,這看起來就像是地圖瀏覽器是你activity所在的應用程式中的一個組成部分,其實它是在另外一個應用程式中定義,並執行在那個應用程式的程序之中的。

Android將這兩個activity放在同一個任務中來維持一個完整的使用者體驗。簡單的說,任務就是使用者所體驗到的“應用程式”。它是安排在一個堆疊中的一組相關的activity。堆疊中的根activity就是啟動了這整個任務的那個──一般情況下,它就是使用者在應用程式載入器中所選擇的。而堆疊最上方的activity則是當前執行的──使用者直接對其進行操作的。當一個activity啟動另外一個的時候,新的activity就被壓入堆疊,併成為當前執行的activity。而前一個activity仍保持在堆疊之中。當用戶按下BACK鍵的時候,當前activity出棧,而前一個恢復為當前執行的activity

堆疊中儲存的其實是物件,所以如果發生了諸如需要多個地圖瀏覽器的情況,就會使得一個任務中出現多個同一Activity子類的例項同時存在,堆疊會為每個例項單獨開闢一個入口。堆疊中的Activity永遠不會重排,只會壓入或彈出。

任務其實就是activity的堆疊,而不是manifest檔案中的一個類或者元素。所以你無法撇開activity而為一個任務設定一個值。而事實上整個任務使用的值是在根activity中設定的。比如說,下一節我們會談及“任務的affinity”,從affinity中讀出的值將會設定到任務的根activity之中。

任務中的所有activity是作為一個整體進行移動的。整個的任務(即

activity堆疊)可以移到前臺,或退至後臺。舉個例子說,比如當前任務在堆疊中存有四個activity──三個在當前activity之下。當用戶按下HOME鍵的時候,回到了應用程式載入器,然後選擇了一個新的應用程式(也就是一個新任務)。則當前任務遁入後臺,而新任務的根activity顯示出來。然後,過了一小會兒,使用者再次回到了應用程式載入器而又選擇了前一個應用程式(上一個任務)。於是那個任務,帶著它堆疊中所有的四個activity,再一次的到了前臺。當用戶按下BACK鍵的時候,螢幕不會顯示出使用者剛才離開的activity(上一個任務的根activity)。取而代之,當前任務的堆疊中最上面的activity被彈出,而同一任務中的上一個activity顯示了出來。

上述的種種即是activity和任務的預設行為模式。但是有一些方法可以改變所有這一切。activity和任務的聯絡、任務中activity的行為方式都被啟動那個activityIntent物件中設定的一系列標記和manifest檔案中那個activity中的元素的系列屬性之間的互動所控制。無論是請求發出者和迴應者在這裡都擁有話語權。

我們剛才所說的這些關鍵Intent標記如下:

FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_RESET_TASK_IF_NEEDEDFLAG_ACTIVITY_SINGLE_TOP

而關鍵的<activity>屬性是:

taskAffinitylaunchModeallowTaskReparentingclearTaskOnLaunchalwaysRetainTaskStatefinishOnTaskLaunch

接下來的一節會描述這些標記以及屬性的作用,它們是如何互相影響的,以及控制它們的使用時必須考慮到的因素。

Affinity(吸引力)和新任務

預設情況下,一個應用程式中的activity相互之間會有一種Affinity──也就是說,它們首選都歸屬於一個任務。然而,可以在<activity>元素中把每個activitytaskAffinity屬性設定為一個獨立的affinity。於是在不同的應用程式中定義的activity可以享有同一個affinity,或者在同一個應用程式中定義的activity有著不同的affinityaffinity在兩種情況下生效:當載入activityIntent物件包含了FLAG_ACTIVITY_NEW_TASK標記,或者當activityallowTaskReparenting屬性設定為“true”

如前所述,在預設情況下,一個新activity被另外一個呼叫了startActivity()方法的activity載入了任務之中。並壓入了呼叫者所在的堆疊。然而,如果傳遞給startActivity()Intent物件包含了FLAG_ACTIVITY_NEW_TASK標記,系統會為新activity安排另外一個任務。一般情況下,如同標記所暗示的那樣,這會是一個新任務。然而,這並不是必然的。如果已經存在了一個與新activity有著同樣affinity的任務,則activity會載入那個任務之中。如果沒有,則啟用新任務。

如果一個activityallowTaskReparenting屬性設定為“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或“singleTopactivity可以被多次初始化。它們可以歸屬於多個任務,而一個任務也可以擁有同一activity的多個例項。

相反,對“singleTask和“singleInstanceactivity被限定於只能有一個例項。因為這些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並將其推入堆疊。

舉例來說,假設一個任務的堆疊由根activityAactivity BC和位於堆疊頂部的D組成,即堆疊A-B-C-D。一個針對D型別的activityintent抵達的時候,如果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物件包含標記,而且目標任務的堆疊中已經存在了一個能夠響應此intentactivity型別的例項。則這個例項之上的所有activity都將被清理以使它位於堆疊的頂部來對intent做出響應。如果此時指定的activity的載入模式為“standard,則它本身也會從堆疊中移除,並載入一個新的例項來處理到來的intent。這是因為載入模式為“standardactivity總會建立一