Android Task Stack模型

Activity和Task的基本模型
  一個Activity可以啓動另一個,即便是定義在不同應用程序中的Activity。例如,假設你想讓用戶顯示一些地方的街景。
而這裏已經有一個Activity可以做到這一點,因此,你的Activity所需要做的只是在Intent對象中添加必要的信息,並傳遞給startActivity()。
地圖瀏覽將會顯示你的地圖。當用戶按下BACK鍵,你的Activity會再次出現在屏幕上。
對於用戶來說,看起來好像是地圖瀏覽與你的Activity一樣,屬於相同的應用程序,即便是它定義在其它的應用程序裏,並運行在那個應用程序的進程裏。
Android通過將這兩個Activity保存在同一個Task裏來體現這一用戶體驗。簡單來說,一個Task就是用戶體驗上的一個“應用”。
它將相關的Activity組合在一起,以stack的方式管理。用Activity啓動Task的一典型就是用戶在應用程序啓動欄中選擇一個Activity。
位於stack頂端的Activity是當前正在運行的——能夠聚焦用戶的動作。當一個Activity啓動另一個新的Activity進入stack;它成爲正在運行的Activity。
之前的Activity仍保留在stack中。當用戶按下BACK鍵,當前的Activity從stack中退出,之前的那個成爲正在運行的Activity。
注意1:一個task並不是對應一個線程,實際上一個task可以包含多個進程。
  一個Task中可能有多個同一個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>元素的特性值交互控制。調用者和響應者都有權決定如何發生。
注意:可以在Activity中通過getTaskId()得到它所在task的Id
核心的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
默認情況下,一個應用程序中(APK)的所有Activity都有相同的affinity(即manifest中的package名)
然而,每個Activity都可以在<activity>元素的taskAffinity特性上設置單獨的值
例如

在程序中可以通過如下的方式得到:

判斷是否是Task的根和得到taskId可以Activity的方法和用如下的方法
定義在不同應用程序中的Activity可以共享同一個affinity,或者定義在同一個應用程序中的Activity設置不同的affinity。
Affinity在兩種環境下工作:Intent對象包含FLAG_ACTIVITY_NEW_TASK標誌,和Activity的allowTaskReparenting特性設置爲“true”。
核心的Intent Flag
FLAG_ACTIVITY_NEW_TASK:
        一個Activity一般通過調用startActivity()啓動並加入到Task中。它同調用者一樣,進入同一個Task。
然而,如果傳遞給startActivity()的Intent對象中包含FLAG_ACTIVITY_NEW_TASK時,系統會搜索一個新的Task來容納新的Activity。
通常,如標誌的名字所示,是一個新的Task。然而,並不是必須是。如果已經存在一個Task與新Activity的affinity相同,這個Activity就會加入到那個Task中。如果不是,啓動一個新的Task。
如果啓動它的acitve和新Activity的affinity相同,那麼新Activity的會進入啓動它的acitve所在的Task.
核心的<activity>特性:
allowTaskReparenting:
如果一個Activity的allowTaskReparenting特性設置爲“true”,它就能從啓動的Task中移到有着相同affinity的Task(這個Task進入到前臺的時候)。
例如,在一個旅遊的程序中定義了一個可以報告選擇城市的天氣情況的Activity。它和同一個應用程序的其它Activity一樣,有着相同的Affinity(默認的Affinity),並且它允許重新宿主。
你的Activity中的一個啓動了天氣預報Activity,因此,它初始化到和你Activity相同的Task中。
如果旅遊應用程序當前沒被啓動,那麼下次它被啓動時,天氣預報那個Activity將會被顯示(即在旅遊應用程序的Task中被顯示)。
如果從用戶的角度出發,一個.apk文件包含多個“應用”的話,你可能希望爲關聯的Activity設置不同的affinity。
Launch Mode
這裏4種不同的啓動模式可以設置到<activity>元素的launchMode特性上:
standard(默認模式)
singleTop
singleTask
singleInstance
注意
1:對於singleTask,如果新的Activity和舊的Activity的affinity一樣時,這時它相當於FLAG_ACTIVITY_NEW_TASK。
注意2:對於singleTask和singleInstance的affinity最好不要和別的task的affinity相沖突。
這些模式有以下四點區別:
1,  哪個Task將容納響應Intent的Activity
    對於“standard”和“singleTop”來說,除非Intent對象包含FLAG_ACTIVITY_NEW_TASK,那麼新的Activity在是產生Intent的那個Task中(並調用startActivity())。
  對於“singleTask”來說,如果它的taskAffinity屬性和啓動它Activity的一樣的話,那麼它是產生在啓動它Activity的Task中(並調用startActivity()),否則它是產生在新Task中,且是該Task的根
    對比而言,“singleInstance”指示Activity總是一個Task的根。
 系統會重新創建一個Task,並把新的Activity加入其中,它們不會加入到另一個Task中。
2,  是否有多個Activity的實例。“standard”和“singleTop”可以實例化多次。它們可以屬於多個Task,一個特定的Task可以有相同Activity的多個實例。
    對比而言,“singleTask”和“singleInstance”只能有一個實例。不可能在多個task中同時存在有該Activity的多個實例。
    也不可能在一個task中存在有該Activity的多個實例。
3,是否可以在同一個Task中擁有其它的Activity。“singleInstance”Activity保持單身,在它的Task中它是僅有的Activity。
    如果它啓動另一個Activity,那個Activity將會放入到不同的Task中,而不管它的啓動模式——好像FLAG_ACTIVITY_NEW_TASK在Intent中一樣。
    其它三個模式允許多個Activity加入到這個Task中。
4, 是否一個新的實例啓動來處理新的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”Activity總是位於stack的頂端(因爲它是task中唯一的Activity),因此,它總是處於能處理Intent的位置。
“singleTask”Activity可acitivity的的,那麼是。這時。
如果在它上方有activity,而這時來個啓動其上方的某個Activity的Intent,那麼是上方的Activity處理該Intent,而不是該“singleTask”Activity。

注意
stack的清除
    如果用戶離開Task很長一段時間,系統會清除Task中的所有Activity,除根Activity外。當用戶再次返回到這個Task時,和用戶離開時不一樣,僅僅只是初始化Activity呈現。
    這樣做的意圖是,經過一些時間後,用戶可能已經忘記之前正在做的事情,並且打算回到Task開始些新的時期。
注意1:彈出任務管理器不算離開Task
這是默認情況。下面一些Activity特性可以用於控制這一行爲並且修改它:
alwaysRetainTaskState
如果Task的根Activity的這個特性設置爲“true”時,上面描述的默認行爲不會發生。Task保留所有的Activity,即便是經過很長一段時間。
clearTaskOnLaunch:
如果Task的根Activity的這個特性設置爲“true”時,當用戶離開Task並返回時,stack會清除直到根Activity。換句話說,它是alwaysRetainTaskState的另一個極端。用戶總是回到Task的初始化狀態,即便是一個短暫的離開。
finishOnTaskLaunch
這個特性和clearTaskOnLaunch相似,但它針對單個Activity,不是整個Task。它能使除了根Activity之外的任何Activity消失。
當它設置爲“true”時,這個Activity僅在當前會話期間保持爲Task的部分。如果用戶離開並再次返回到這個Task,它就不再顯示了。
注意:對於clearTaskOnLaunch和finishOnTaskLaunch所謂的清除總是在該task被再次啓動時執行的。
這裏還有其它的方式可以強制Activity從stack中移除。如果Intent對象中包含FLAG_ACTIVITY_CLEAR_TOP標誌,並且目標Task中已經有一個這個類型Activity的實例,
而且這個實例應該處理這個Intent,那麼,位於其上的Activity都將移除,這樣,這個Activity就能在stack的頂端並響應這個Intent。
如果這個Activity的啓動模式設定爲“standard”,它也會從stack中清除,然後新的實例啓動來響應這個Intent。
這是因爲當啓動模式設定爲“standard“時,總是會創建一個新的實例來響應新的Intent。
FLAG_ACTIVITY_CLEAR_TOP經常與FLAG_ACTIVITY_NEW_TASK結合起來使用。當一起使用時,這些標誌可以定位其它Task中已經存在的Activity,並且把它置於可以響應Intent的位置。
啓動Task
如果一個Activity的Intent Filter的action爲“android.intent.action.MAIN”、category爲“android.intent.category.LAUNCHER”時,
它就可以作爲一個Task的入口點。有這種類型的Filter會在導致這個Activity在應用程序啓動欄顯示一個圖標和標籤,給用戶提供一個方式可以啓動這個Task和在任何時候可以再次回到這個Task。
第二個能力很重要:用戶一定可以離開一個Task,然後可以再次回到它。
基於這個原因,“singleInstance”應該只在有MAIN和LAUNCHER的Activity上使用。
“singleTask”在沒有MAIN和LAUNCHER的Activity上使用,應該讓它和入口Activity(有MAIN和LAUNCHER的Activity)的taskAffinity一致
例如,假設這個Filter沒有且它和入口Activity的taskAffinity又不一致的話:一個Intent啓動了一個“singleTask”Activity,初始化一個新的Task,然後用戶花費了一些時間在它上面。然後,用戶按下HOME鍵。現在,這個Task處於後臺並且被HOME畫面遮蓋。
由於它不能在應用程序啓動欄顯示,用戶就沒有辦法可以返回它。
注意1:
注意2:
在面對FLAG_ACTIVITY_NEW_TASK時,也有相似的困難。如果這個標誌導致一個Activity啓動了一個新的Task,並且用戶按下HOME鍵離開它,這裏必須有方法可以再次回到它。
一些機能(如Notification Manager)總是在外部的Task中啓動Activity,而不是作爲自己的一部分,因此,它總是把FLAG_ACTIVITY_NEW_TASK標誌放入Intent,然後傳遞給startActivity()。
如果你的Activity可能會被外部的機能(可能使用這個標誌)調用,注意用戶可以額外的方式可以返回到啓動的Task。
如果你不想用戶回到某個Activity,可以把<activity>元素的finishOnTaskLaunch設置爲“true”。
非Activity Context啓動Activity
    對於Context的startActivity方法,如果不是在其子類(Activity)中調用,那麼必須對Intent加上FLAG_ACTIVITY_NEW_TASK
具體可以參照Context中對startActivity方法的說明:
public void startActivity (Intent intent)
    Launch a new activity. You will not receive any information about when the activity exits.
Note that if this method is being called from outside of an Activity Context, 
then the Intent must include the FLAG_ACTIVITY_NEW_TASK launch flag. This is because, without being started from an existing Activity, there is no existing task in which to place the new activity and thus it needs to be placed in its own separate task.
This method throws ActivityNotFoundException if there was no Activity found to run the given Intent.
Parameters
intent     The description of the activity to start.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章