Android 關于launchMode、intent flag和taskAffinity

LaunchMode

1、standard

? ? ? ? standard啟動模式為最基本的啟動模式,默認為該種啟動模式,特點就是每當發送一個intent請求打開該activity時,都會創建一個新的activity實例。實際使用情況分為兩種,一種是本應用打開,一種是跨應用打開:

-本應用打開,新創建的activity實例放入本應用,即發送intent的task棧的頂部

-跨引用打開,這里有一個需要注意的地方是跨應用打開的時候會在 Recent app 頁面顯示兩個獨立項,但是此時它們兩個 Activity 仍然是在一個棧中

1

如想要保持在一個recent app可以設置啟動啟動intent的flag包含:

?FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

此時兩個Activity仍在同一棧中。

2

2、singleTop

? ? ? ?singleTop其實和standard幾乎一樣,和standard算一組,使用singleTop的Activity也可以創建很多個實例。唯一不同的就是,如果調用的目標Activity已經位于調用者的Task的棧頂,則不創建新實例,而是使用當前的這個Activity實例,并調用這個實例的onNewIntent方法。

? ? ? ? 這個使用場景比較少,可以使用的例子比如用戶已經在當前activity,用戶點擊一條推送消息之后也需要跳轉到當前activity,那么為了避免activity的重復打開,則需要將該activity設置為singleTop并且復寫onNewIntent即可。

如果是外部程序啟動singleTop的Activity,在Android 5.0之前新創建的Activity會位于調用者的Task中,5.0及以后會放入新的Task中,這點和standard一樣。

3、singleTask

? ? ? ? 使用singleTask啟動模式的Activity在系統中只會存在一個實例。如果這個實例已經存在,intent就會通過onNewIntent傳遞到這個Activity,并且將棧中該activity之上的activity清除(銷毀過程會調用Activity生命周期回調),如果不存在新的Activity實例將被創建。

3

實際使用情況也分為兩組:

-本應用啟動,在一個應用中啟動設置為singleTask的activity,如果該activity在task棧中不存在,則會創建一個新的實例放在棧頂,如果在activity的task棧中已經存在了該activity實例,則會將棧中該activity實例之上的其他activity實例清空,并且會調用該activity的onNewIntent方法。最常用的使用例子就是首頁。

使用提示:此時onNewIntent方法中不能進行fragment的相關操作,否則IllegalStateException: Can not perform this action after onSaveInstanceState,可在OnResume中根據intent再進行相關操作。

-跨應用啟動,由于整個系統只能存在activity的一個實例,所以如果系統中不存在該activity,則會啟動一個新的task去啟動該activity,并且將該activity放入棧底。如果系統中存在該activity實例,則會直接啟動該activity,調用該activity的onNewIntent的方法,同時將該activity task棧上面的其他activity清空(銷毀過程會調用Activity生命周期回調),此時如果用戶摁下返回鍵,那么將在singleTask activity的task棧中操作,即返回該棧中singleTask activity的上一個activity直到該棧中無activity時才會返回到最開始啟動singleTask activity的activity中。還有一種情況是singleTask Activity所在的應用進程存在,但是singleTask Activity實例不存在,那么從別的應用啟動這個Activity,新的Activity實例會被創建,并放入到所屬進程所在的Task中,并位于棧頂位置。注意如果使用了singleTask,FLAG_ACTIVITY_RESET_TASK_IF_NEEDED這個flag將會失效。

需要特別注意的是:

1.在4.x和之前的系統下,A1(startActivityForResult)->A2(singleTask, startActivityForResult)->A3->A4,當A1打開A2之后會立即回調onActivityResult()函數,返回RESULT_CANCELED,

Logcat輸出如下的信息:

Activity is launching as a new task, so cancelling activity result

A2打開A3仍然可以正?;卣{onActivityResult();但是從5.0開始,A1打開A2的時候 onActivityResult() 函數也能正常的回調,不會立即回調。

4、singleInstance

? ? ? ? 和singleTask類似,在系統中只會存在一個實例,唯一的區別就是系統不會在singleInstance activity的task棧中啟動任何其他的activity,singleInstance activity棧中僅僅只能有該activity的實例,其他任何從這個activity啟動的activity都會在其他的棧中被打開。雖然使用adb shell dumpsys activity可以看到singleInstance activity在一個獨立的task中,但是在任務管理器中,還是顯示的一個。

4

需要特別注意的是:

1)、A1->A2(SingleInstance),摁下 Home 鍵之后,點擊應用圖標再次進入應用,返回的是 A1 頁面,這是因為 A2 在另一個單獨的 Activity task 棧中,點擊圖標返回的是主 Activity棧(根據taskAffinity),所以此時顯示的 A1 頁面,而不是 A2 頁面。

2)、注意singleInstance的返回鍵的處理和上面3個 mode 有區別,如果是使用正常的 startActivity 進行的啟動,啟動順序A1->A2->A3(singleInstance)->A4,在A4頁面摁下back鍵,返回的是A2,再返回A1,接著再次摁下back鍵,返回的才是A3;如果是使用 startActivityForResult 啟動的,在4.x和之前的系統下,表現和之前是一樣的,但是在5.0開始,A1->A2-A3(singleInstance + startActivityForResult)->A4,在A4摁下返回鍵,返回的是A3->A2->A1,這個需要著重說明一下。

3.另一個比較重要的是,在4.x和之前的系統下,A1(startActivityForResult)->A2(singleInstance, startActivityForResult)->A3->A4,當A1打開A2之后會立即回調onActivityResult()函數,情況類似singleTask,A2打開A3也會立即回調onActivityResult()函數;但是從5.0開始,A1打開A2和A2打開A3的兩種情況下 onActivityResult() 函數都能正常的回調,不會立即回調。

IntentFlag

FLAG_ACTIVITY_BROUGHT_TO_FRONT

? ? ? ? 比方說我現在有A,在A中啟動B,在A中Intent中加上這個標記。此時B就是以FLAG_ACTIVITY_BROUGHT_TO_FRONT 這個啟動的,在B中再啟動C,D(正常啟動C,D),如果這個時候在D中再啟動B,這個時候最后的棧的情況是 A,C,D,B。

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_CLEAR_TASK

? ? ? ? 如果在調用startActivity時傳遞這個標記,該task棧中的其他activity會先被清空,然后該activity在該task中啟動,也就是說,這個新啟動的activity變為了這個空task的根activity。所有老的activity都結束掉。該標志必須和FLAG_ACTIVITY_NEW_TASK一起使用。

FLAG_ACTIVITY_CLEAR_TOP

? ? ? ? 如果該activity已經在task中存在,并且設置了該task,系統不會啟動新的 Activity 實例,會將task棧里該Activity之上的所有Activity一律結束掉,然后將Intent發給這個已存在的Activity。Activity收到 Intent之后,或者在onNewIntent()里做下一步的處理,或者自行結束然后重新創建。如 Activity 在 AndroidMainifest.xml 里將啟動模式設置成默認standard模式,且 Intent 里也沒有設置 FLAG_ACTIVITY_SINGLE_TOP,那么Activity將會結束并且重啟;否則則會傳遞到onNewIntent方法。

已經啟動了四個Activity:A,B,C和D。在D Activity里,我們要跳到B Activity,同時希望C finish掉,可以在startActivity(intent)里的intent里添加flags標記,這樣啟動B Activity,就會把D,C都finished掉,如果你的B Activity的啟動模式是默認的(multiple) ,則B Activity會finished掉,再啟動一個新的Activity B。? 如果不想重新再創建一個新的B Activity,則可在啟動Intent添加flag FLAG_ACTIVITY_SINGLE_TOP。

? ? ? ? 可以利用此特性來退出程序,假設A為程序入口,將A的Manifest.xml配置成android:launchMode="singleTop",通過此flag來啟動A,同時在A的onNewIntent中判斷來結束自己,可到退出的效果。FLAG_ACTIVITY_CLEAR_TOP 還可以和 FLAG_ACTIVITY_NEW_TASK 配合使用,用來啟動一個task棧的根activity,他將會把該棧清空為根狀態,比如從notification manager啟動activity。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

? ? ? ? 設置完之后,新的activity將不會添加到當前activity列表中,當某些情況下我們不希望用戶通過歷史列表回到我們的Activity的時候這個標記比較有用。他等同于在XML中指定Activity的屬性android:excludeFromRecents=”true”

FLAG_ACTIVITY_FORWARD_RESULT

? ? ? ? 如果A需要onActivityResult中獲取返回結果,startActivityForResult B,而B只是過渡頁,啟動C之后就finish掉了,需要在 C 中setResult返回給A就可以用到這個標志。

A -> B -> XXXXX(無論多少個過渡頁) 設置 FLAG_ACTIVITY_FORWARD_RESULT 來啟動 C ,之后該XXX過渡頁finish - > C ,那么C的結果返回給A。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY

? ? ? ? 如果設置,新的Activity將不再歷史stack中保留。用戶一離開它,這個Activity就關閉了。例如A啟動B的時候,給B設置了FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY,那么:

A -> B -> C ,啟動C 就算 B沒有自行finish ,也會變為 AC

FLAG_ACTIVITY_MULTIPLE_TASK

? ? ? ? 這個標識用來創建一個新的task棧,并且在里面啟動新的activity(所有情況,不管系統中存在不存在該activity實例),經常和FLAG_ACTIVITY_NEW_DOCUMENT或者FLAG_ACTIVITY_NEW_TASK一起使用。這上面兩種使用場景下,如果沒有帶上FLAG_ACTIVITY_MULTIPLE_TASK標識,他們都會使系統搜索存在的task棧,去尋找匹配intent的一個activity,如果沒有找到就會去新建一個task棧;但是當和FLAG_ACTIVITY_MULTIPLE_TASK一起使用的時候,這兩種場景都會跳過搜索這步操作無條件的創建一個新的task。和FLAG_ACTIVITY_NEW_TASK一起使用需要注意,盡量不要使用該組合除非你完成了自己的頂部應用啟動器,他們的組合使用會禁用已經存在的task?;氐角芭_的功能。

FLAG_ACTIVITY_NEW_DOCUMENT

? ? ? ? api 21之后加入的一個標識,用來在intent啟動的activity的task棧中打開一個document,和documentLaunchMode效果相等,有著不同的documents的activity的多個實例,將會出現在最近的task列表中。

documentLaunchMode可以設置4個值

intoExisting: activity 會為該document請求一個已經存在的task,這與設置FLAG_ACTIVITY_NEW_DOCUMENT且不設置FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果。

always: activity 會為該document創建一個新的task,即使該document已經被打開了,這與設置 FLAG_ACTIVITY_NEW_DOCUMENT且設置 FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果。

none:activity 不會為 document 創建新的task,該app被設置為 single task 的模式,它會重新調用用戶喚醒的所有activity中的最近的一個。

never:activity 不會為document創建一個新的task,設置這個值復寫了 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 標簽。如果其中一個標簽被設置,并且overview screen 顯示該app為 single task 模式。則該activity會重新調用用戶最近喚醒的activity。

注意: none 或 nerver 使用時,activity必須設置為 launchMode=”standard” ,如果該屬性沒有設置,documentLaunchMode=”none” 屬性就會被使用。有

FLAG_ACTIVITY_RETAIN_IN_RECENTS

? ? ? ? api21加入。默認情況下通過FLAG_ACTIVITY_NEW_DOCUMENT啟動的activity在關閉之后,task中的記錄會相對應的刪除。如果為了能夠重新啟動這個activity你想保留它,就可以使用者個flag,最近的記錄將會保留在接口中以便用戶去重新啟動。接受該flag的activity可以使用autoRemoveFromRecents去復寫這個request或者調用Activity.finishAndRemoveTask()方法。

FLAG_ACTIVITY_NEW_TASK

? ? ? ? 設置此狀態,記住以下原則,首先會查找是否存在和被啟動的Activity具有相同的親和性的任務棧(即taskAffinity,注意同一個應用程序中的activity的親和性在沒有修改的情況下是一樣的,所以下面的a情況會在同一個棧中),如果有,剛直接把這個棧整體移動到前臺,并保持棧中的狀態不變,即棧中的activity順序不變,如果沒有,則新建一個棧來存放被啟動的activity。

a. 前提: Activity A和Activity B在同一個應用中。

操作: Activity A啟動開僻Task堆棧(堆棧狀態:A),在Activity A中啟動Activity B, 啟動Activity B的Intent的Flag設為FLAG_ACTIVITY_NEW_TASK,Activity B被壓入Activity A所在堆棧(堆棧狀態:AB)。

原因: 默認情況下同一個應用中的所有Activity擁有相同的關系(taskAffinity)。

b. 前提: Activity A在名稱為”TaskOne應用”的應用中, Activity C和Activity D在名稱為”TaskTwo應用”的應用中。

操作1:在Launcher中單擊“TaskOne應用”圖標,Activity A啟動開僻Task堆棧,命名為TaskA(TaskA堆棧狀態: A),在Activity A中啟動Activity C, 啟動Activity C的Intent的Flag設為FLAG_ACTIVITY_NEW_TASK,Android系統會為Activity C開僻一個新的Task,命名為TaskB(TaskB堆棧狀態: C), 長按Home鍵,選擇TaskA,Activity A回到前臺, 再次啟動Activity C(兩種情況:1.從桌面啟動;2.從Activity A啟動,兩種情況一樣), 這時TaskB回到前臺, Activity C顯示,供用戶使用, 即:包含FLAG_ACTIVITY_NEW_TASK的Intent啟動Activity的Task正在運行,則不會為該Activity創建新的Task,而是將原有的Task返回到前臺顯示。

操作2:在Launcher中單擊”TaskOne應用”圖標,Activity A啟動開僻Task堆棧,命名為TaskA(TaskA堆棧狀態: A),在Activity A中啟動Activity C,啟動Activity C的Intent的Flag設為FLAG_ACTIVITY_NEW_TASK,Android系統會為Activity C開僻一個新的Task,命名為TaskB(TaskB堆棧狀態: C), 在Activity C中啟動Activity D(TaskB的狀態: CD) 長按Home鍵, 選擇TaskA,Activity A回到前臺, 再次啟動Activity C(從桌面或者ActivityA啟動,也是一樣的),這時TaskB回到前臺, Activity D顯示,供用戶使用。說明了在此種情況下設置FLAG_ACTIVITY_NEW_TASK后,會先查找是不是有Activity C存在的棧,根據親和性(taskAffinity),如果有,剛直接把這個棧整體移動到前臺,并保持棧中的狀態不變,即棧中的順序不變。

FLAG_ACTIVITY_NO_ANIMATION

? ? ? ? 啟動的時候不執行動畫。

FLAG_ACTIVITY_NO_USER_ACTION

? ? ? ? 禁止activity調用onUserLeaveHint()函。onUserLeaveHint()作為activity周期的一部分,它在activity因為用戶要跳轉到別的activity而退到background時使用。比如,在用戶按下Home鍵(用戶的操作),它將被調用。比如有電話進來(不屬于用戶的操作),它就不會被調用。注意:通過調用finish()時該activity銷毀時不會調用該函數。

FLAG_ACTIVITY_PREVIOUS_IS_TOP

? ? ? ? 如果給Intent對象設置了這個標記,這個Intent對象被用于從一個存在的Activity中啟動一個新的Activity,那么新的這個Activity不能用于接受發送給頂層activity的intent,這個新的activity的前一個activity被作為頂部activity。

FLAG_ACTIVITY_TASK_ON_HOME

? ? ? ? api11加入。把當前新啟動的任務置于Home任務之上,也就是按back鍵從這個任務返回的時候會回到home,即使這個不是他們最后看見的activity,注意這個標記必須和FLAG_ACTIVITY_NEW_TASK一起使用。

FLAG_EXCLUDE_STOPPED_PACKAGESFLAG_INCLUDE_STOPPED_PACKAGES

? ? ? ? 從Android 3.1開始,給Intent定義了兩個新的Flag,分別為FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,用來控制Intent是否要對處于停止狀態的App起作用,顧名思義:

FLAG_INCLUDE_STOPPED_PACKAGES:表示包含未啟動的App

FLAG_EXCLUDE_STOPPED_PACKAGES:表示不包含未啟動的App

值得注意的是,Android 3.1開始,系統向所有Intent的廣播添加了FLAG_EXCLUDE_STOPPED_PACKAGES標志。這樣做是為了防止廣播無意或不必要地開啟未啟動App的后臺服務。如果要強制調起未啟動的App,后臺服務或應用程序可以通過向廣播Intent添加FLAG_INCLUDE_STOPPED_PACKAGES標志來喚醒。

taskAffinity

? ? ? ? 每個Activity都有taskAffinity屬性,這個屬性指出了它希望進入的Task。如果一個Activity沒有顯式的指明該 Activity的taskAffinity,那么它的這個屬性就等于Application指明的taskAffinity,如果 Application也沒有指明,那么該taskAffinity的值就等于包名。而Task也有自己的affinity屬性,它的值等于它的根 Activity的taskAffinity的值。

? ? ? ? taskAffinity屬性主要和singleTask啟動模式或者allowTaskReparenting屬性配對使用,在其他情況下沒有意義。當taskAffinity和singleTask啟動模式配對使用的時候,他是具有該模式的Activity的目前任務棧的名字,待啟動的Activity會運行在名字和TaskAffinity相同的任務棧中。allowTaskReparenting用于配置是否允許該activity可以更換從屬task,通常情況二者連在一起使用,用于實現把一個應用程序的Activity移到另一個應用程序的Task中。allowTaskReparenting用來標記Activity能否從啟動的Task移動到taskAffinity指定的Task,默認是繼承至application中的allowTaskReparenting=false,如果為true,則表示可以更換;false表示不可以。

? ? ? ? 如果加載某個Activity的intent,Flag被設置成FLAG_ACTIVITY_NEW_TASK時,它會首先檢查是否存在與自己taskAffinity相同的Task,如果存在,那么它會直接宿主到該Task中,如果不存在則重新創建Task。

引用

http://blog.csdn.net/self_study/article/details/48055011

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容