Android的Task和Activity相關

心鑫發表於2013-12-16

android:allowTaskReparenting

    用來標記Activity能否從啟動的Task移動到有著affinity的Task(當這個Task進入到前臺時)——“true”,表示能移動,“false”,表示它必須呆在啟動時呆在的那個Task裡。
    如果這個特性沒有被設定,設定到<application>元素上的allowTaskReparenting特性的值會應用到Activity上。預設值為“false”。
    一般來說,當Activity啟動後,它就與啟動它的Task關聯,並且在那裡耗盡它的整個生命週期。噹噹前的Task不再顯示時,你可以使用這個特性來強制Activity移動到有著affinity的Task中。典型用法是:把一個應用程式的Activity移到另一個應用程式的主Task中。
    例如,如果e-mail中包含一個web頁的連結,點選它就會啟動一個Activity來顯示這個頁面。這個Activity是由Browser應用程式定義的,但是,現在它作為e-mail Task的一部分。如果它重新宿主到Browser Task裡,當Browser下一次進入到前臺時,它就能被看見,並且,當e-mail Task再次進入前臺時,就看不到它了。
    Actvity的affinity是由taskAffinity特性定義的。Task的affinity是通過讀取根Activity的affinity決定。因此,根據定義,根Activity總是位於相同affinity的Task裡。由於啟動模式為“singleTask”和“singleInstance”的Activity只能位於Task的底部,因此,重新宿主只能限於“standard”和“singleTop”模式。

android:alwaysRetainTaskState
    用來標記Activity所在的Task的狀態是否總是由系統來保持——“true”,表示總是;“false”,表示在某種情形下允許系統恢復Task到它的初始化狀態。預設值是“false”。這個特性只針對Task的根Activity有意義;對其它Activity來說,忽略之。
    一般來說,特定的情形如當使用者從主畫面重新選擇這個Task時,系統會對這個Task進行清理(從stack中刪除位於根Activity之上的所有Activivity)。典型的情況,當使用者有一段時間沒有訪問這個Task時也會這麼做,例如30分鐘。
    然而,當這個特性設為“true”時,使用者總是能回到這個Task的最新狀態,無論他們是如何啟動的。這非常有用,例如,像Browser應用程式,這裡有很多的狀態(例如多個開啟的Tab),使用者不想丟失這些狀態。

android:clearTaskOnLaunch
    用來標記是否從Task中清除所有的Activity,除了根Activity外(每當從主畫面重新啟動時)——“true”,表示總是清除至它的根Activity,“false”表示不。預設值是“false”。這個特性只對啟動一個新的Task的Activity(根Activity)有意義;對Task中其它的Activity忽略。
    當這個值為“true”,每次使用者重新啟動這個Task時,都會進入到它的根Activity中,不管這個Task最後在做些什麼,也不管使用者是使用BACK還是HOME離開的。當這個值為“false”時,可能會在一些情形下(參考alwaysRetainTaskState特性)清除Task的Activity,但不總是。
    假設,某人從主畫面啟動了Activity P,並從那裡遷移至Activity Q。接下來使用者按下HOME,然後返回Activity P。一般,使用者可能見到的是Activity Q,因為它是P的Task中最後工作的內容。然而,如果P設定這個特性為“true”,當使用者按下HOME並使這個Task再次進入前臺時,其上的所有的Activity(在這裡是Q)都將被清除。因此,當返回到這個Task時,使用者只能看到P。
    如果這個特性和allowTaskReparenting都設定為“true”,那些能重新宿主的Activity會移動到共享affinity的Task中;剩下的Activity都將被拋棄,如上所述。

android:finishOnTaskLaunch
    用來標記當使用者再次啟動它的Task(在主畫面選擇這個Task)時已經存在的Activity例項是否要關閉(結束)——“true”,表示應該關閉,“false”表示不關閉。預設值是“false”。
    如果這個特性和allowTaskReparenting都設定為“true”,這個特性勝出。Activity的affinity忽略。這個Activity不會重新宿主,但是會銷燬。

android:launchMode
    用於指示Activity如何啟動。這裡有四種模式,與Intent物件中的Activity Flags(FLAG_ACTIVITY_*變數)共同作用,來決定Activity如何啟動來處理Intent。它們是:

    "standard"
    "singleTop"
    "singleTask"
    "singleInstance"

    預設模式是“standard”。
    
    這些模式可以分成兩大組別,“standard”和“singleTop”一組,“singleTask”和“singleInstance”一組。具有“standard”和“singleTop”啟動模式的Activity可以例項化很多次。這些例項可以屬於任何Task並且可以位於Activity stack的任何位置。典型的情況是,它們會進入呼叫startActivity()的Task(除非Intent物件包含FLAG_ACTIVITY_NEW_TASK標誌,在這種情況下會選擇一個不同的Task——參考taskAffinity特性)。
    相反的,“singleTask”和“singleInstance”只能啟動一個Task。它們總是位於Activity stack的底部。甚至,裝置一次只能擁有一個Activity的例項——只有一個這樣的Task。
    “standard”和“singleTop”模式只在一種情況下有差別:每次有一個新的啟動“standard”Activity的Intent,就會建立一個新的例項來響應這個Intent。每個例項處理一個Intent。相似的,一個“singleTop”的Activity例項也有可能被建立來處理新的Intent。然而,如果目標Task已經有一個存在的例項並且位於stack的頂部,那麼,這個例項就會接收到這個新的Intent(呼叫onNewIntent());不會建立新的例項。在其他情況下——例如,如果存在的“singleTop”的Activity例項在目標Task中,但不是在stack的頂部,或者它在一個stack的頂部,但不是在目標Task中——新的例項都會被建立並壓入stack中。
    “singleTask”和“singleInstance”模式也只在一種情況下有差別:“singleTask”Activity允許其它Activity成為它的Task的部分。它位於Activity stack的底部,其它Activity(必須是“standard”和“singleTop”Activity)可以啟動加入到相同的Task中。“singleInstance”Activity,換句話說,不允許其它Activity成為它的Task的部分。它是Task中的唯一Activity。如果它啟動其它的Activity,這個Activity會被放置到另一個task中——好像Intent中包含了FLAG_ACTIVITY_NEW_TASK標誌。

android:noHistory
    用於標記當使用者從Activity上離開並且它在螢幕上不再可見時Activity是否從Activity stack中清除並結束(呼叫finish()方法)——“true”,表示它應該關閉,“false”,表示不需要。預設值是“false”。
    “true”值意味著Activity不會留下歷史痕跡。因為它不會在Activity stack的Task中保留,因此,使用者不能返回它。

android:taskAffinity
   Activity為Task擁有的一個affinity。擁有相同的affinity的Activity理論上屬於相同的Task(在使用者的角度是相同的“應用程式”)。Task的affinity是由它的根Activity決定的。 
   affinity決定兩件事情——Activity重新宿主的Task(參考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK標誌啟動的Activity宿主的Task。
    預設情況,一個應用程式中的所有Activity都擁有相同的affinity。捏可以設定這個特性來重組它們,甚至可以把不同應用程式中定義的Activity放置到相同的Task中。為了明確Activity不宿主特定的Task,設定該特性為空的字串。
    如果這個特性沒有設定,Activity將從應用程式的設定那裡繼承下來(參考<application>元素的taskAffinity特性)。應用程式預設的affinity的名字是<manifest>元素中設定的package名。

FLAG_ACTIVITY_BROUGHT_TO_FRONT 
    這個標誌一般不是由程式程式碼設定的,如在launchMode中設定singleTask模式時系統幫你設定。

FLAG_ACTIVITY_CLEAR_TOP
    如果設定,並且這個Activity已經在當前的Task中執行,因此,不再是重新啟動一個這個Activity的例項,而是在這個Activity上方的所有Activity都將關閉,然後這個Intent會作為一個新的Intent投遞到老的Activity(現在位於頂端)中。
    例如,假設一個Task中包含這些Activity:A,B,C,D。如果D呼叫了startActivity(),並且包含一個指向Activity B的Intent,那麼,C和D都將結束,然後B接收到這個Intent,因此,目前stack的狀況是:A,B。
    上例中正在執行的Activity B既可以在onNewIntent()中接收到這個新的Intent,也可以把自己關閉然後重新啟動來接收這個Intent。如果它的啟動模式宣告為“multiple”(預設值),並且你沒有在這個Intent中設定FLAG_ACTIVITY_SINGLE_TOP標誌,那麼它將關閉然後重新建立;對於其它的啟動模式,或者在這個Intent中設定FLAG_ACTIVITY_SINGLE_TOP標誌,都將把這個Intent投遞到當前這個例項的onNewIntent()中。
    這個啟動模式還可以與FLAG_ACTIVITY_NEW_TASK結合起來使用:用於啟動一個Task中的根Activity,它會把那個Task中任何執行的例項帶入前臺,然後清除它直到根Activity。這非常有用,例如,當從Notification Manager處啟動一個Activity。

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
    如果設定,這將在Task的Activity stack中設定一個還原點,當Task恢復時,需要清理Activity。也就是說,下一次Task帶著FLAG_ACTIVITY_RESET_TASK_IF_NEEDED標記進入前臺時(典型的操作是使用者在主畫面重啟它),這個Activity和它之上的都將關閉,以至於使用者不能再返回到它們,但是可以回到之前的Activity。
    這在你的程式有分割點的時候很有用。例如,一個e-mail應用程式可能有一個操作是檢視一個附件,需要啟動圖片瀏覽Activity來顯示。這個Activity應該作為e-mail應用程式Task的一部分,因為這是使用者在這個Task中觸發的操作。然而,當使用者離開這個Task,然後從主畫面選擇e-mail app,我們可能希望回到檢視的會話中,但不是檢視圖片附件,因為這讓人困惑。通過在啟動圖片瀏覽時設定這個標誌,瀏覽及其它啟動的Activity在下次使用者返回到mail程式時都將全部清除。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    如果設定,新的Activity不會在最近啟動的Activity的列表中儲存。

FLAG_ACTIVITY_FORWARD_RESULT
    如果設定,並且這個Intent用於從一個存在的Activity啟動一個新的Activity,那麼,這個作為答覆目標的Activity將會傳到這個新的Activity中。這種方式下,新的Activity可以呼叫setResult(int),並且這個結果值將傳送給那個作為答覆目標的Activity。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 
    這個標誌一般不由應用程式程式碼設定,如果這個Activity是從歷史記錄裡啟動的(常按HOME鍵),那麼,系統會幫你設定。

FLAG_ACTIVITY_MULTIPLE_TASK 
    不要使用這個標誌,除非你自己實現了應用程式啟動器。與FLAG_ACTIVITY_NEW_TASK結合起來使用,可以禁用把已存的Task送入前臺的行為。當設定時,新的Task總是會啟動來處理Intent,而不管這是是否已經有一個Task可以處理相同的事情。
    由於預設的系統不包含圖形Task管理功能,因此,你不應該使用這個標誌,除非你提供給使用者一種方式可以返回到已經啟動的Task。
    如果FLAG_ACTIVITY_NEW_TASK標誌沒有設定,這個標誌被忽略。

FLAG_ACTIVITY_NEW_TASK 
    如果設定,這個Activity會成為歷史stack中一個新Task的開始。一個Task(從啟動它的Activity到下一個Task中的Activity)定義了使用者可以遷移的Activity原子組。Task可以移動到前臺和後臺;在某個特定Task中的所有Activity總是保持相同的次序。
    這個標誌一般用於呈現“啟動”型別的行為:它們提供使用者一系列可以單獨完成的事情,與啟動它們的Activity完全無關。
    使用這個標誌,如果正在啟動的Activity的Task已經在執行的話,那麼,新的Activity將不會啟動;代替的,當前Task會簡單的移入前臺。參考FLAG_ACTIVITY_MULTIPLE_TASK標誌,可以禁用這一行為。
    這個標誌不能用於呼叫方對已經啟動的Activity請求結果。

FLAG_ACTIVITY_NO_ANIMATION 
    如果在Intent中設定,並傳遞給Context.startActivity()的話,這個標誌將阻止系統進入下一個Activity時應用Acitivity遷移動畫。這並不意味著動畫將永不執行——如果另一個Activity在啟動顯示之前,沒有指定這個標誌,那麼,動畫將被應用。這個標誌可以很好的用於執行一連串的操作,而動畫被看作是更高一級的事件的驅動。

FLAG_ACTIVITY_NO_HISTORY 
    如果設定,新的Activity將不再歷史stack中保留。使用者一離開它,這個Activity就關閉了。這也可以通過設定noHistory特性。

FLAG_ACTIVITY_NO_USER_ACTION 
    如果設定,作為新啟動的Activity進入前臺時,這個標誌將在Activity暫停之前阻止從最前方的Activity回撥的onUserLeaveHint()。
    典型的,一個Activity可以依賴這個回撥指明顯式的使用者動作引起的Activity移出後臺。這個回撥在Activity的生命週期中標記一個合適的點,並關閉一些Notification。
    如果一個Activity通過非使用者驅動的事件,如來電或鬧鐘,啟動的,這個標誌也應該傳遞給Context.startActivity,保證暫停的Activity不認為使用者已經知曉其Notification。

FLAG_ACTIVITY_PREVIOUS_IS_TOP 
    If set and this intent is being used to launch a new activity from an existing one, the current activity will not be counted as the top activity for deciding whether the new intent should be delivered to the top instead of starting a new one. The previous activity will be used as the top, with the assumption being that the current activity will finish itself immediately. 

FLAG_ACTIVITY_REORDER_TO_FRONT
    如果在Intent中設定,並傳遞給Context.startActivity(),這個標誌將引發已經執行的Activity移動到歷史stack的頂端。
    例如,假設一個Task由四個Activity組成:A,B,C,D。如果D呼叫startActivity()來啟動Activity B,那麼,B會移動到歷史stack的頂端,現在的次序變成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP標誌也設定的話,那麼這個標誌將被忽略。

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed. 

FLAG_ACTIVITY_SINGLE_TOP
    如果設定,當這個Activity位於歷史stack的頂端執行時,不再啟動一個新的。 

Activity和Task

 

之前提到的,一個Activity可以啟動另一個,即便是定義在不同應用程式中的Activity。例如,假設你想讓使用者顯示一些地方的街景。而這裡已經有一個Activity可以做到這一點,因此,你的Activity所需要做的只是在Intent物件中新增必要的資訊,並傳遞給startActivity()。地圖瀏覽將會顯示你的地圖。當使用者按下BACK鍵,你的Activity會再次出現在螢幕上。

 

對於使用者來說,看起來好像是地圖瀏覽與你的Activity一樣,屬於相同的應用程式,即便是它定義在其它的應用程式裡,並執行在那個應用程式的程式裡。Android通過將這兩個Activity儲存在同一個Task裡來體現這一使用者體驗。簡單來說,一個Task就是使用者體驗上的一個“應用”。它將相關的Activity組合在一起,以stack的方式管理。stack中根Activity啟動Task——典型的,它就是使用者在應用程式啟動欄中選擇的Activity。位於stack頂端的Activity是當前正在執行的——能夠聚焦使用者的動作。當一個Activity啟動另一個,新的Activity進入stack;它成為正在執行的Activity。之前的Activity仍保留在stack中。當使用者按下BACK鍵,當前的Activity從stack中退出,之前的那個成為正在執行的Activity。

 

stack包含物件,因此,如果一個Task中有多個同一個Activity的例項時——多個地圖瀏覽,例如——stack為每個例項擁有一個獨立的入口。位於stack中的Activity不會重新調整,只是進入和退出。

 

一個Task就是一組Activity,不是一個類或者在manifest中定義的一個元素。因此,沒有辦法為Task設定獨立於它的Activity的屬性值。Task的值作為整體在根Activity中設定。例如,下一個章節會討論Task的“affinity”;那個值就是從Task中的根Activity中讀取的。

 

Task中的所有Activity作為一個單元一起移動。整個Task(整個Activity stack)可以進入前臺或者退到後臺。例如,假設當前Task中的stack中有4個Activity——3個位於當前Activity下方。使用者按下HOME鍵,進入到應用程式啟動欄,然後選擇一個新的應用程式(實際上,一個新的Task)。當前Task退到後臺,並且新Task中的根Activity會顯示出來。然後,經過一段時間後,使用者回到Home畫面,然後再次選擇前一個應用程式(前一個Task)。那個擁有4個Activity的Task會進入前臺。當使用者按下BACK鍵,螢幕不會顯示使用者剛剛離開的Activity(前一個Task的根Activity)。而是,這個stack中的頂端Activity移除,相同Task中的前一個Activity會顯示出來。

 

剛才描述的行為是Activity和Task的預設行為。但有方法來完全改變它。Task之間的關聯,和一個Task中的一個Activity行為,受啟動Activity的Intent物件中設定的Flag和manifest檔案中Activity的<activity>元素的特性值互動控制。呼叫者和響應者都有權決定如何發生。

 

核心的Intent Flag有:

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

FLAG_ACTIVITY_SINGLE_TOP

 

核心的<activity>特性有:

taskAffinity

launchMode

allowTaskReparenting

clearTaskOnLaunch

alwaysRetainTaskState

finishOnTaskLaunch

 

接下來的章節將描述一些Flag和特性的用法,如何相互影響,以及在使用時的建議。

 

Affinity和新Task

預設情況下,一個應用程式中的所有Activity都有affinity——也就是說,屬於同一個Task中所有Activity有一個設定。然而,每個Activity都可以在<activity>元素的taskAffinity特性上設定單獨的值。定義在不同應用程式中的Activity可以共享同一個affinity,或者定義在同一個應用程式中的Activity設定不同的affinity。Affinity在兩種環境下工作:Intent物件包含FLAG_ACTIVITY_NEW_TASK標誌,和Activity的allowTaskReparenting特性設定為“true”。

FLAG_ACTIVITY_NEW_TASK:

之前描述的,一個Activity一般通過呼叫startActivity()啟動並加入到Task中。它同呼叫者一樣,進入同一個Task。然而,如果傳遞給startActivity()的Intent物件中包含FLAG_ACTIVITY_NEW_TASK時,系統會搜尋一個新的Task來容納新的Activity。通常,如標誌的名字所示,是一個新的Task。然而,並不是必須是。如果已經存在一個Task與新Activity的affinity相同,這個Activity就會加入到那個Task中。如果不是,啟動一個新的Task。

allowTaskReparenting:

如果一個Activity的allowTaskReparenting特性設定為“true”,它就能從啟動的Task中移到有著相同affinity的Task(這個Task進入到前臺的時候)。例如,在一個旅遊的程式中定義了一個可以報告選擇城市的天氣情況的Activity。它和同一個應用程式的其它Activity一樣,有著相同的Affinity(預設的Affinity),並且它允許重新宿主。你的Activity中的一個啟動了天氣預報,因此,它初始化到和你Activity相同的Task中。然而,當旅遊應用程式下一次進入到前臺時,天氣預報那個Activity將會重新編排並在那個Task中顯示。

 

如果從使用者的角度出發,一個.apk檔案包含多個“應用”的話,你可能希望為關聯的Activity設定不同的affinity。

 

Launch Mode

 

這裡4種不同的啟動模式可以設定到<activity>元素的launchMode特性上:

standard(預設模式)

singleTop

singleTask

singleInstance

 

這些模式有以下四點區別:

l  哪個Task將容納響應Intent的Activity。對於“standard”和“singleTop”來說,是產生Intent的那個Task(並呼叫startActivity())——除非Intent物件包含FLAG_ACTIVITY_NEW_TASK。在那種情況下,不同的Task將被選擇,如“Affinity和新Task”中描述的那樣。對比而言,“singleTask”和“singleInstance”指示Activity總是一個Task的根。它們定義一個Task;它們不會加入到另一個Task中。

l  是否有多個Activity的例項。“standard”和“singleTop”可以例項化多次。它們可以屬於多個Task,一個特定的Task可以有相同Activity的多個例項。對比而言,“singleTask”和“singleInstance”只能有一個例項。因為這些Activity只能位於Task的底部,這一限制意味著在裝置的某個時間,不會出現這樣Task的多個例項。

l  是否可以在同一個Task中擁有其它的Activity。“singleInstance”Activity保持單身,在它的Task中它是僅有的Activity。如果它啟動另一個Activity,那個Activity將會放入到不同的Task中,而不管它的啟動模式——好像FLAG_ACTIVITY_NEW_TASK在Intent中一樣。對於其它方面,,“singleInstance”等同於“singleTask”。其它三個模式允許多個Activity加入到這個Task中。“singleTask”Activity總是位於Task的底部,但它可以啟動其它的Activity並放入到它的Task中。“standard”和“singleTop”的Activity可以出現在stack的任何地方。

l  是否一個新的例項啟動來處理新的Intent。對於預設的“standard”來說,都是建立一個新的例項來響應新的Intent。每個例項處理一個Intent。對於“singleTop”來說,如果它位於目標Task的頂端,那麼,已經存在的例項就可以重複使用來處理這個新的Intent。如果它不在頂端,那麼它就不能重複使用。替代的,新的例項將建立來響應新的Intent,並進入到stack中。

例如,假設一個Task的Activity stack中包含根Activity A和其它Activity B,C,D,並且D位於頂端,因此,stack是A-B-C-D。有一個Intent來了,它要啟動D型別的Activity。如果D有預設的“standard”啟動模式,那麼,一個新的例項將被啟動並且stack變成A-B-C-D-D。然而,如果D的啟動模式“singleTop”,已經存在的例項將去處理新來的Intent(因為它正好處在stack的頂端),並且stack依舊是A-B-C-D。

換句話說,如果來臨的Intent是衝著B型別的,那麼,B型別的例項將被建立啟動而不管B的模式是“standard”或“singleTop”(因為B不處在stack的頂端),因此,stack將會是A-B-C-D-B。

之前提到的,裝置上不會出現超過一個例項的“singleTask”或“singleInstance”Activity,因此,那個例項都將去處理所有新來的Intent。“singleInstance alimama_pid="mm_15626785_2176297_8615857"; alimama_titlecolor="3366CC"; alimama_descolor ="000000"; alimama_bgcolor="FFFFFF"; alimama_bordercolor="CCCCCC"; alimama_linkcolor="008000"; alimama_bottomcolor="FFFFFF"; alimama_anglesize="0"; alimama_bgpic="0"; alimama_icon="0"; alimama_sizecode="9999"; alimama_width=468; alimama_height=90; alimama_type=2;

相關文章