LaunchMode
1锄开、standard
? ? ? ? standard啟動模式為最基本的啟動模式古沥,默認(rèn)為該種啟動模式瘸右,特點(diǎn)就是每當(dāng)發(fā)送一個intent請求打開該activity時娇跟,都會創(chuàng)建一個新的activity實(shí)例。實(shí)際使用情況分為兩種太颤,一種是本應(yīng)用打開苞俘,一種是跨應(yīng)用打開:
-本應(yīng)用打開,新創(chuàng)建的activity實(shí)例放入本應(yīng)用龄章,即發(fā)送intent的task棧的頂部
-跨引用打開吃谣,這里有一個需要注意的地方是跨應(yīng)用打開的時候會在 Recent app 頁面顯示兩個獨(dú)立項(xiàng),但是此時它們兩個 Activity 仍然是在一個棧中
如想要保持在一個recent app可以設(shè)置啟動啟動intent的flag包含:
?FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
此時兩個Activity仍在同一棧中瓦堵。
2基协、singleTop
? ? ? ?singleTop其實(shí)和standard幾乎一樣,和standard算一組菇用,使用singleTop的Activity也可以創(chuàng)建很多個實(shí)例澜驮。唯一不同的就是,如果調(diào)用的目標(biāo)Activity已經(jīng)位于調(diào)用者的Task的棧頂惋鸥,則不創(chuàng)建新實(shí)例杂穷,而是使用當(dāng)前的這個Activity實(shí)例,并調(diào)用這個實(shí)例的onNewIntent方法卦绣。
? ? ? ? 這個使用場景比較少耐量,可以使用的例子比如用戶已經(jīng)在當(dāng)前activity,用戶點(diǎn)擊一條推送消息之后也需要跳轉(zhuǎn)到當(dāng)前activity滤港,那么為了避免activity的重復(fù)打開廊蜒,則需要將該activity設(shè)置為singleTop并且復(fù)寫onNewIntent即可。
如果是外部程序啟動singleTop的Activity溅漾,在Android 5.0之前新創(chuàng)建的Activity會位于調(diào)用者的Task中山叮,5.0及以后會放入新的Task中,這點(diǎn)和standard一樣添履。
3屁倔、singleTask
? ? ? ? 使用singleTask啟動模式的Activity在系統(tǒng)中只會存在一個實(shí)例。如果這個實(shí)例已經(jīng)存在暮胧,intent就會通過onNewIntent傳遞到這個Activity锐借,并且將棧中該activity之上的activity清除(銷毀過程會調(diào)用Activity生命周期回調(diào)),如果不存在新的Activity實(shí)例將被創(chuàng)建往衷。
實(shí)際使用情況也分為兩組:
-本應(yīng)用啟動钞翔,在一個應(yīng)用中啟動設(shè)置為singleTask的activity,如果該activity在task棧中不存在席舍,則會創(chuàng)建一個新的實(shí)例放在棧頂布轿,如果在activity的task棧中已經(jīng)存在了該activity實(shí)例,則會將棧中該activity實(shí)例之上的其他activity實(shí)例清空,并且會調(diào)用該activity的onNewIntent方法驮捍。最常用的使用例子就是首頁疟呐。
使用提示:此時onNewIntent方法中不能進(jìn)行fragment的相關(guān)操作,否則IllegalStateException: Can not perform this action after onSaveInstanceState东且,可在OnResume中根據(jù)intent再進(jìn)行相關(guān)操作启具。
-跨應(yīng)用啟動,由于整個系統(tǒng)只能存在activity的一個實(shí)例珊泳,所以如果系統(tǒng)中不存在該activity鲁冯,則會啟動一個新的task去啟動該activity,并且將該activity放入棧底色查。如果系統(tǒng)中存在該activity實(shí)例薯演,則會直接啟動該activity,調(diào)用該activity的onNewIntent的方法秧了,同時將該activity task棧上面的其他activity清空(銷毀過程會調(diào)用Activity生命周期回調(diào))跨扮,此時如果用戶摁下返回鍵,那么將在singleTask activity的task棧中操作验毡,即返回該棧中singleTask activity的上一個activity直到該棧中無activity時才會返回到最開始啟動singleTask activity的activity中衡创。還有一種情況是singleTask Activity所在的應(yīng)用進(jìn)程存在,但是singleTask Activity實(shí)例不存在晶通,那么從別的應(yīng)用啟動這個Activity璃氢,新的Activity實(shí)例會被創(chuàng)建,并放入到所屬進(jìn)程所在的Task中狮辽,并位于棧頂位置一也。注意如果使用了singleTask,FLAG_ACTIVITY_RESET_TASK_IF_NEEDED這個flag將會失效喉脖。
需要特別注意的是:
1.在4.x和之前的系統(tǒng)下椰苟,A1(startActivityForResult)->A2(singleTask, startActivityForResult)->A3->A4,當(dāng)A1打開A2之后會立即回調(diào)onActivityResult()函數(shù)动看,返回RESULT_CANCELED尊剔,
Logcat輸出如下的信息:
Activity is launching as a new task, so cancelling activity result
A2打開A3仍然可以正匙茫回調(diào)onActivityResult()菱皆;但是從5.0開始,A1打開A2的時候 onActivityResult() 函數(shù)也能正常的回調(diào)挨稿,不會立即回調(diào)仇轻。
4、singleInstance
? ? ? ? 和singleTask類似奶甘,在系統(tǒng)中只會存在一個實(shí)例篷店,唯一的區(qū)別就是系統(tǒng)不會在singleInstance activity的task棧中啟動任何其他的activity,singleInstance activity棧中僅僅只能有該activity的實(shí)例,其他任何從這個activity啟動的activity都會在其他的棧中被打開疲陕。雖然使用adb shell dumpsys activity可以看到singleInstance activity在一個獨(dú)立的task中方淤,但是在任務(wù)管理器中,還是顯示的一個蹄殃。
需要特別注意的是:
1)携茂、A1->A2(SingleInstance),摁下 Home 鍵之后诅岩,點(diǎn)擊應(yīng)用圖標(biāo)再次進(jìn)入應(yīng)用讳苦,返回的是 A1 頁面,這是因?yàn)?A2 在另一個單獨(dú)的 Activity task 棧中吩谦,點(diǎn)擊圖標(biāo)返回的是主 Activity棧(根據(jù)taskAffinity)鸳谜,所以此時顯示的 A1 頁面,而不是 A2 頁面式廷。
2)咐扭、注意singleInstance的返回鍵的處理和上面3個 mode 有區(qū)別,如果是使用正常的 startActivity 進(jìn)行的啟動滑废,啟動順序A1->A2->A3(singleInstance)->A4草描,在A4頁面摁下back鍵,返回的是A2策严,再返回A1穗慕,接著再次摁下back鍵,返回的才是A3妻导;如果是使用 startActivityForResult 啟動的逛绵,在4.x和之前的系統(tǒng)下,表現(xiàn)和之前是一樣的倔韭,但是在5.0開始术浪,A1->A2-A3(singleInstance + startActivityForResult)->A4,在A4摁下返回鍵寿酌,返回的是A3->A2->A1胰苏,這個需要著重說明一下。
3.另一個比較重要的是醇疼,在4.x和之前的系統(tǒng)下硕并,A1(startActivityForResult)->A2(singleInstance, startActivityForResult)->A3->A4,當(dāng)A1打開A2之后會立即回調(diào)onActivityResult()函數(shù),情況類似singleTask秧荆,A2打開A3也會立即回調(diào)onActivityResult()函數(shù)倔毙;但是從5.0開始,A1打開A2和A2打開A3的兩種情況下 onActivityResult() 函數(shù)都能正常的回調(diào)乙濒,不會立即回調(diào)陕赃。
IntentFlag
FLAG_ACTIVITY_BROUGHT_TO_FRONT
? ? ? ? 比方說我現(xiàn)在有A,在A中啟動B,在A中Intent中加上這個標(biāo)記么库。此時B就是以FLAG_ACTIVITY_BROUGHT_TO_FRONT 這個啟動的傻丝,在B中再啟動C,D(正常啟動C诉儒,D)桑滩,如果這個時候在D中再啟動B,這個時候最后的棧的情況是 A,C,D,B允睹。
FLAG_ACTIVITY_REORDER_TO_FRONT
? ? ? ? 如果在Intent中設(shè)置运准,并傳遞給Context.startActivity(),這個標(biāo)志將引發(fā)已經(jīng)運(yùn)行的Activity移動到歷史stack的頂端缭受。 例如胁澳,假設(shè)一個Task由四個Activity組成:A,B米者,C韭畸,D。如果D調(diào)用startActivity()來啟動Activity B蔓搞,那么胰丁,B會移動到歷史stack的頂端,現(xiàn)在的次序變成A喂分,C锦庸,D,B蒲祈。如果FLAG_ACTIVITY_CLEAR_TOP標(biāo)志也設(shè)置的話甘萧,那么這個標(biāo)志將被覆蓋。
? ? ? ? 如果在調(diào)用startActivity時傳遞這個標(biāo)記梆掸,該task棧中的其他activity會先被清空扬卷,然后該activity在該task中啟動,也就是說酸钦,這個新啟動的activity變?yōu)榱诉@個空task的根activity怪得。所有老的activity都結(jié)束掉。該標(biāo)志必須和FLAG_ACTIVITY_NEW_TASK一起使用卑硫。
? ? ? ? 如果該activity已經(jīng)在task中存在徒恋,并且設(shè)置了該task,系統(tǒng)不會啟動新的 Activity 實(shí)例拔恰,會將task棧里該Activity之上的所有Activity一律結(jié)束掉因谎,然后將Intent發(fā)給這個已存在的Activity基括。Activity收到 Intent之后颜懊,或者在onNewIntent()里做下一步的處理,或者自行結(jié)束然后重新創(chuàng)建。如 Activity 在 AndroidMainifest.xml 里將啟動模式設(shè)置成默認(rèn)standard模式河爹,且 Intent 里也沒有設(shè)置 FLAG_ACTIVITY_SINGLE_TOP匠璧,那么Activity將會結(jié)束并且重啟;否則則會傳遞到onNewIntent方法咸这。
已經(jīng)啟動了四個Activity:A夷恍,B,C和D媳维。在D Activity里酿雪,我們要跳到B Activity,同時希望C finish掉侄刽,可以在startActivity(intent)里的intent里添加flags標(biāo)記指黎,這樣啟動B Activity,就會把D州丹,C都finished掉醋安,如果你的B Activity的啟動模式是默認(rèn)的(multiple) ,則B Activity會finished掉墓毒,再啟動一個新的Activity B吓揪。? 如果不想重新再創(chuàng)建一個新的B Activity,則可在啟動Intent添加flag FLAG_ACTIVITY_SINGLE_TOP所计。
? ? ? ? 可以利用此特性來退出程序柠辞,假設(shè)A為程序入口,將A的Manifest.xml配置成android:launchMode="singleTop"主胧,通過此flag來啟動A钾腺,同時在A的onNewIntent中判斷來結(jié)束自己,可到退出的效果讥裤。FLAG_ACTIVITY_CLEAR_TOP 還可以和 FLAG_ACTIVITY_NEW_TASK 配合使用放棒,用來啟動一個task棧的根activity,他將會把該棧清空為根狀態(tài)己英,比如從notification manager啟動activity间螟。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
? ? ? ? 設(shè)置完之后,新的activity將不會添加到當(dāng)前activity列表中损肛,當(dāng)某些情況下我們不希望用戶通過歷史列表回到我們的Activity的時候這個標(biāo)記比較有用厢破。他等同于在XML中指定Activity的屬性android:excludeFromRecents=”true”
? ? ? ? 如果A需要onActivityResult中獲取返回結(jié)果,startActivityForResult B治拿,而B只是過渡頁摩泪,啟動C之后就finish掉了,需要在 C 中setResult返回給A就可以用到這個標(biāo)志劫谅。
A -> B -> XXXXX(無論多少個過渡頁) 設(shè)置 FLAG_ACTIVITY_FORWARD_RESULT 來啟動 C 见坑,之后該XXX過渡頁finish - > C 嚷掠,那么C的結(jié)果返回給A。
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
? ? ? ? 如果設(shè)置荞驴,新的Activity將不再歷史stack中保留不皆。用戶一離開它,這個Activity就關(guān)閉了熊楼。例如A啟動B的時候霹娄,給B設(shè)置了FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY,那么:
A -> B -> C 鲫骗,啟動C 就算 B沒有自行finish 犬耻,也會變?yōu)?AC
? ? ? ? 這個標(biāo)識用來創(chuàng)建一個新的task棧,并且在里面啟動新的activity(所有情況执泰,不管系統(tǒng)中存在不存在該activity實(shí)例)香追,經(jīng)常和FLAG_ACTIVITY_NEW_DOCUMENT或者FLAG_ACTIVITY_NEW_TASK一起使用。這上面兩種使用場景下坦胶,如果沒有帶上FLAG_ACTIVITY_MULTIPLE_TASK標(biāo)識透典,他們都會使系統(tǒng)搜索存在的task棧,去尋找匹配intent的一個activity顿苇,如果沒有找到就會去新建一個task棧峭咒;但是當(dāng)和FLAG_ACTIVITY_MULTIPLE_TASK一起使用的時候,這兩種場景都會跳過搜索這步操作無條件的創(chuàng)建一個新的task纪岁。和FLAG_ACTIVITY_NEW_TASK一起使用需要注意凑队,盡量不要使用該組合除非你完成了自己的頂部應(yīng)用啟動器,他們的組合使用會禁用已經(jīng)存在的task椺:玻回到前臺的功能漩氨。
? ? ? ? api 21之后加入的一個標(biāo)識,用來在intent啟動的activity的task棧中打開一個document遗增,和documentLaunchMode效果相等叫惊,有著不同的documents的activity的多個實(shí)例,將會出現(xiàn)在最近的task列表中做修。
documentLaunchMode可以設(shè)置4個值
intoExisting: activity 會為該document請求一個已經(jīng)存在的task霍狰,這與設(shè)置FLAG_ACTIVITY_NEW_DOCUMENT且不設(shè)置FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果。
always: activity 會為該document創(chuàng)建一個新的task饰及,即使該document已經(jīng)被打開了蔗坯,這與設(shè)置 FLAG_ACTIVITY_NEW_DOCUMENT且設(shè)置 FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果。
none:activity 不會為 document 創(chuàng)建新的task燎含,該app被設(shè)置為 single task 的模式宾濒,它會重新調(diào)用用戶喚醒的所有activity中的最近的一個。
never:activity 不會為document創(chuàng)建一個新的task屏箍,設(shè)置這個值復(fù)寫了 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 標(biāo)簽绘梦。如果其中一個標(biāo)簽被設(shè)置橘忱,并且overview screen 顯示該app為 single task 模式。則該activity會重新調(diào)用用戶最近喚醒的activity谚咬。
注意: none 或 nerver 使用時鹦付,activity必須設(shè)置為 launchMode=”standard” 尚粘,如果該屬性沒有設(shè)置择卦,documentLaunchMode=”none” 屬性就會被使用。有
FLAG_ACTIVITY_RETAIN_IN_RECENTS
? ? ? ? api21加入郎嫁。默認(rèn)情況下通過FLAG_ACTIVITY_NEW_DOCUMENT啟動的activity在關(guān)閉之后秉继,task中的記錄會相對應(yīng)的刪除。如果為了能夠重新啟動這個activity你想保留它泽铛,就可以使用者個flag尚辑,最近的記錄將會保留在接口中以便用戶去重新啟動。接受該flag的activity可以使用autoRemoveFromRecents去復(fù)寫這個request或者調(diào)用Activity.finishAndRemoveTask()方法盔腔。
? ? ? ? 設(shè)置此狀態(tài)杠茬,記住以下原則,首先會查找是否存在和被啟動的Activity具有相同的親和性的任務(wù)棧(即taskAffinity弛随,注意同一個應(yīng)用程序中的activity的親和性在沒有修改的情況下是一樣的瓢喉,所以下面的a情況會在同一個棧中),如果有舀透,剛直接把這個棧整體移動到前臺栓票,并保持棧中的狀態(tài)不變,即棧中的activity順序不變愕够,如果沒有走贪,則新建一個棧來存放被啟動的activity。
a. 前提: Activity A和Activity B在同一個應(yīng)用中惑芭。
操作: Activity A啟動開僻Task堆棧(堆棧狀態(tài):A)睡腿,在Activity A中啟動Activity B, 啟動Activity B的Intent的Flag設(shè)為FLAG_ACTIVITY_NEW_TASK伴网,Activity B被壓入Activity A所在堆棧(堆棧狀態(tài):AB)合呐。
原因: 默認(rèn)情況下同一個應(yīng)用中的所有Activity擁有相同的關(guān)系(taskAffinity)。
b. 前提: Activity A在名稱為”TaskOne應(yīng)用”的應(yīng)用中漩勤, Activity C和Activity D在名稱為”TaskTwo應(yīng)用”的應(yīng)用中感挥。
操作1:在Launcher中單擊“TaskOne應(yīng)用”圖標(biāo),Activity A啟動開僻Task堆棧越败,命名為TaskA(TaskA堆棧狀態(tài): A)触幼,在Activity A中啟動Activity C, 啟動Activity C的Intent的Flag設(shè)為FLAG_ACTIVITY_NEW_TASK究飞,Android系統(tǒng)會為Activity C開僻一個新的Task置谦,命名為TaskB(TaskB堆棧狀態(tài): C), 長按Home鍵堂鲤,選擇TaskA,Activity A回到前臺, 再次啟動Activity C(兩種情況:1.從桌面啟動媒峡;2.從Activity A啟動瘟栖,兩種情況一樣), 這時TaskB回到前臺, Activity C顯示谅阿,供用戶使用, 即:包含F(xiàn)LAG_ACTIVITY_NEW_TASK的Intent啟動Activity的Task正在運(yùn)行半哟,則不會為該Activity創(chuàng)建新的Task,而是將原有的Task返回到前臺顯示签餐。
操作2:在Launcher中單擊”TaskOne應(yīng)用”圖標(biāo)寓涨,Activity A啟動開僻Task堆棧,命名為TaskA(TaskA堆棧狀態(tài): A)氯檐,在Activity A中啟動Activity C戒良,啟動Activity C的Intent的Flag設(shè)為FLAG_ACTIVITY_NEW_TASK,Android系統(tǒng)會為Activity C開僻一個新的Task冠摄,命名為TaskB(TaskB堆棧狀態(tài): C)糯崎, 在Activity C中啟動Activity D(TaskB的狀態(tài): CD) 長按Home鍵, 選擇TaskA河泳,Activity A回到前臺沃呢, 再次啟動Activity C(從桌面或者ActivityA啟動,也是一樣的)乔询,這時TaskB回到前臺, Activity D顯示樟插,供用戶使用。說明了在此種情況下設(shè)置FLAG_ACTIVITY_NEW_TASK后竿刁,會先查找是不是有Activity C存在的棧黄锤,根據(jù)親和性(taskAffinity),如果有食拜,剛直接把這個棧整體移動到前臺鸵熟,并保持棧中的狀態(tài)不變,即棧中的順序不變负甸。
? ? ? ? 啟動的時候不執(zhí)行動畫流强。
? ? ? ? 禁止activity調(diào)用onUserLeaveHint()函。onUserLeaveHint()作為activity周期的一部分呻待,它在activity因?yàn)橛脩粢D(zhuǎn)到別的activity而退到background時使用打月。比如,在用戶按下Home鍵(用戶的操作)蚕捉,它將被調(diào)用奏篙。比如有電話進(jìn)來(不屬于用戶的操作),它就不會被調(diào)用。注意:通過調(diào)用finish()時該activity銷毀時不會調(diào)用該函數(shù)秘通。
? ? ? ? 如果給Intent對象設(shè)置了這個標(biāo)記为严,這個Intent對象被用于從一個存在的Activity中啟動一個新的Activity,那么新的這個Activity不能用于接受發(fā)送給頂層activity的intent肺稀,這個新的activity的前一個activity被作為頂部activity第股。
? ? ? ? api11加入。把當(dāng)前新啟動的任務(wù)置于Home任務(wù)之上话原,也就是按back鍵從這個任務(wù)返回的時候會回到home夕吻,即使這個不是他們最后看見的activity,注意這個標(biāo)記必須和FLAG_ACTIVITY_NEW_TASK一起使用稿静。
FLAG_EXCLUDE_STOPPED_PACKAGES和FLAG_INCLUDE_STOPPED_PACKAGES
? ? ? ? 從Android 3.1開始梭冠,給Intent定義了兩個新的Flag辕狰,分別為FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES改备,用來控制Intent是否要對處于停止?fàn)顟B(tài)的App起作用,顧名思義:
FLAG_INCLUDE_STOPPED_PACKAGES:表示包含未啟動的App
FLAG_EXCLUDE_STOPPED_PACKAGES:表示不包含未啟動的App
值得注意的是蔓倍,Android 3.1開始悬钳,系統(tǒng)向所有Intent的廣播添加了FLAG_EXCLUDE_STOPPED_PACKAGES標(biāo)志。這樣做是為了防止廣播無意或不必要地開啟未啟動App的后臺服務(wù)偶翅。如果要強(qiáng)制調(diào)起未啟動的App默勾,后臺服務(wù)或應(yīng)用程序可以通過向廣播Intent添加FLAG_INCLUDE_STOPPED_PACKAGES標(biāo)志來喚醒。
taskAffinity
? ? ? ? 每個Activity都有taskAffinity屬性聚谁,這個屬性指出了它希望進(jìn)入的Task母剥。如果一個Activity沒有顯式的指明該 Activity的taskAffinity,那么它的這個屬性就等于Application指明的taskAffinity形导,如果 Application也沒有指明环疼,那么該taskAffinity的值就等于包名。而Task也有自己的affinity屬性朵耕,它的值等于它的根 Activity的taskAffinity的值炫隶。
? ? ? ? taskAffinity屬性主要和singleTask啟動模式或者allowTaskReparenting屬性配對使用,在其他情況下沒有意義阎曹。當(dāng)taskAffinity和singleTask啟動模式配對使用的時候伪阶,他是具有該模式的Activity的目前任務(wù)棧的名字,待啟動的Activity會運(yùn)行在名字和TaskAffinity相同的任務(wù)棧中处嫌。allowTaskReparenting用于配置是否允許該activity可以更換從屬task栅贴,通常情況二者連在一起使用,用于實(shí)現(xiàn)把一個應(yīng)用程序的Activity移到另一個應(yīng)用程序的Task中熏迹。allowTaskReparenting用來標(biāo)記Activity能否從啟動的Task移動到taskAffinity指定的Task檐薯,默認(rèn)是繼承至application中的allowTaskReparenting=false,如果為true癣缅,則表示可以更換厨剪;false表示不可以哄酝。
? ? ? ? 如果加載某個Activity的intent,F(xiàn)lag被設(shè)置成FLAG_ACTIVITY_NEW_TASK時祷膳,它會首先檢查是否存在與自己taskAffinity相同的Task陶衅,如果存在,那么它會直接宿主到該Task中直晨,如果不存在則重新創(chuàng)建Task搀军。