Activity 啟動(dòng)模式及任務(wù)棧探究
在Android中剔应,默認(rèn)情況下喘鸟,當(dāng)我們啟動(dòng)一個(gè)Activity的時(shí)候越除,系統(tǒng)會(huì)創(chuàng)建一個(gè)Activity實(shí)例然后將它放入任務(wù)棧(任務(wù)棧是一個(gè) “后進(jìn)先出” 的棧結(jié)構(gòu))中棒坏,當(dāng)我們按 back 鍵返回時(shí)臭挽,這些Activity就會(huì)一一回退八匠。但是當(dāng)我們重復(fù)啟動(dòng)一個(gè)Activity多次時(shí)絮爷,就會(huì)創(chuàng)建多個(gè)該Activity的實(shí)例,同樣返回時(shí)要多次返回一個(gè)一個(gè)退出才行梨树,在很多情況下坑夯,這種模式是很不友好的,所以Android就提供了啟動(dòng)模式來(lái)修改系統(tǒng)默認(rèn)的行為抡四。
定義 Activity 的啟動(dòng)模式
可以通過(guò)兩種方式定義不同的啟動(dòng)模式:
-
使用清單文件
在清單文件中聲明 Activity 時(shí)柜蜈,修改
launchMode
屬性值來(lái)達(dá)到修改啟動(dòng)模式的作用。該方式下有四種值可戎秆病:standard淑履、singleTop、singleTask藻雪、singleInstance秘噪。 -
使用 Intent 標(biāo)志
調(diào)用
startActivity()
時(shí),可以在 Intent 中加入一個(gè)標(biāo)志(flag)勉耀,來(lái)達(dá)到修改啟動(dòng)模式的作用指煎。該方式下Intent有比較多的 Intent.FLAG_XXX 可取蹋偏,如:Intent.FLAG_ACTIVITY_NEW_TASK
、Intent.FLAG_ACTIVITY_CLEAR_TOP
等具體的可以查看源碼至壤。
注1:某些適用于清單文件的啟動(dòng)模式不可用作 Intent 標(biāo)志威始,同樣,某些可用作 Intent 標(biāo)志的啟動(dòng)模式無(wú)法在清單文件中定義(如:清單文件形式無(wú)法直接為 Activity 設(shè)置為 Intent.FLAG_ACTIVITY_CLEAR_TOP
標(biāo)識(shí)像街,而Intent標(biāo)識(shí)無(wú)法指定Activity為 singleInstance
模式)黎棠。
注2:如果同時(shí)給一個(gè)Activity使用了上述兩種方法指定啟動(dòng)模式,那么使用 Intent 標(biāo)識(shí)的優(yōu)先級(jí)更高宅广。
在清單文件中給Activity設(shè)置 launchMode
屬性的值
在清單文件中聲明 Activity 時(shí)葫掉,您可以使用 <activity> 元素的 launchMode 屬性指定 Activity 應(yīng)該如何與任務(wù)關(guān)聯(lián)。
launchMode 屬性指定有關(guān)應(yīng)如何將 Activity 啟動(dòng)到任務(wù)中的指令跟狱。您可以分配給 launchMode 屬性的啟動(dòng)模式共有四種:
standard 模式
標(biāo)準(zhǔn)啟動(dòng)模式俭厚,這種啟動(dòng)模式也是Activity默認(rèn)的。每次啟動(dòng)Activity時(shí)都會(huì)重新創(chuàng)建一個(gè)實(shí)例驶臊,不管棧中是否存在該Activity實(shí)例挪挤。這也就是說(shuō),一個(gè)任務(wù)棧中可以有多個(gè)實(shí)例关翎,同時(shí)扛门,每個(gè)實(shí)例也可以屬于不同的任務(wù)棧。就是誰(shuí)啟動(dòng)的它纵寝,那么它就運(yùn)行在啟動(dòng)它的那個(gè)Activity所在的任務(wù)棧中论寨。
有一個(gè)特殊情況,那就是如果啟動(dòng)它的Activity本身是 singleInstance
模式爽茴,那么就不能運(yùn)行在啟動(dòng)它的任務(wù)棧中了葬凳,會(huì)出現(xiàn)以下兩種情況:
- ① 它會(huì)運(yùn)行在默認(rèn)的任務(wù)棧中,如果默認(rèn)棧還沒(méi)有創(chuàng)建室奏,那就會(huì)新建任務(wù)棧火焰,然后運(yùn)行在任務(wù)棧中;
- ② 或者在清單文件中給它設(shè)置了
taskAffinity
屬性胧沫,他就會(huì)先判斷該任務(wù)棧是否存在昌简,如果存在,就在運(yùn)行在該棧中绒怨;不存在纯赎,就新建指定的任務(wù)棧,然后運(yùn)行在該任務(wù)棧中南蹂。
注意:有時(shí)候我們?cè)?Service 中啟動(dòng) standard 模式的Activity時(shí)址否,就會(huì)報(bào)如下錯(cuò):
android.util.AndroidRuntimeException:
Calling startActivity() from outside of an Activity context requires
the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
這是因?yàn)?standard 模式的Activity運(yùn)行在啟動(dòng)它的任務(wù)棧中,而非Activity的Context沒(méi)有任務(wù)棧,那么就會(huì)有問(wèn)題了佑附,解決這個(gè)問(wèn)題的方法就是為待啟動(dòng)的Activity指定一個(gè) FLAG_ACTIVITY_NEW_TASK
標(biāo)記位樊诺,實(shí)際效果相當(dāng)于給待啟動(dòng)的Activity設(shè)置了 singleTask 啟動(dòng)模式。
singleTop 模式
單一棧頂啟動(dòng)模式, 或者棧頂復(fù)用模式音同,棧頂只能存在一個(gè)相同的Activity词爬。如果當(dāng)前任務(wù)棧的頂部已存在 Activity 的實(shí)例,則系統(tǒng)會(huì)通過(guò)調(diào)用該實(shí)例的 onNewIntent()
方法向其傳送 Intent权均,而不是創(chuàng)建 Activity 的新實(shí)例顿膨,生命周期的 onCreate()、onStart()
等方法不會(huì)被回調(diào)叽赊。如果新的Activity實(shí)例已存在但不是在任務(wù)棧的頂部恋沃,那么新的Activity仍然會(huì)被創(chuàng)建。
舉個(gè)例子:假設(shè)當(dāng)前任務(wù)棧包含根 Activity A 以及 Activity B必指、C 和位于頂部的 D(堆棧是 A-B-C-D囊咏;D 位于頂部)。收到針對(duì) D 類(lèi) Activity 的 Intent塔橡。如果 D 具有默認(rèn)的 "standard" 啟動(dòng)模式梅割,則會(huì)啟動(dòng)該類(lèi)的新實(shí)例,且堆棧會(huì)變成 A-B-C-D-D葛家。但是户辞,如果 D 的啟動(dòng)模式是 "singleTop",則 D 的現(xiàn)有實(shí)例會(huì)通過(guò) onNewIntent() 接收 Intent癞谒,因?yàn)樗挥诙褩5捻敳康琢牵欢褩H詾?A-B-C-D。但是弹砚,如果收到針對(duì) B 類(lèi) Activity 的 Intent双仍,則會(huì)向堆棧添加 B 的新實(shí)例,即便Activity B 的其啟動(dòng)模式為 "singleTop" 也是如此迅栅,堆棧將變?yōu)?A-B-C-D-B殊校。
singleTask 模式
單一棧啟動(dòng)模式晴玖,或者棧內(nèi)復(fù)用模式读存,在這種模式下,只要Activity在棧中存在呕屎,那么多次啟動(dòng)該Activity都不會(huì)重建實(shí)例让簿,而是和 singleTop 模式一樣,系統(tǒng)會(huì)通過(guò)調(diào)用該實(shí)例的 onNewIntent()
方法向其傳送 Intent秀睛。
具體一點(diǎn)尔当,當(dāng)一個(gè)Activity A是 singleTask 模式時(shí),啟動(dòng)它時(shí),系統(tǒng)首先會(huì)尋找Activity A所需要的任務(wù)棧椭迎,如果任務(wù)棧不存在锐帜,就會(huì)新建任務(wù)棧,然后在新建 Activity A 實(shí)例并把它放到棧中畜号;如果所需要任務(wù)棧已經(jīng)存在缴阎,那就看Activity A的實(shí)例是否已經(jīng)存在,如果存在那么就會(huì)將Activity A調(diào)到棧頂(其實(shí)是將Activity A 實(shí)例之上的其他實(shí)例移除出棧)并調(diào)用它的 onNewIntent()
方法简软,如果不存在就新建Activity A 實(shí)例并放入棧中蛮拔。
舉個(gè)例子:假設(shè)當(dāng)前任務(wù)棧包含根 Activity A以及 Activity B 、C 和 位于頂部的 D(堆棧是 A-B-C-D)痹升,Activity B的啟動(dòng)模式為 singleTask建炫,那么當(dāng)我們?cè)俅螁?dòng) Activity B時(shí),就不會(huì)在創(chuàng)建Activity B的實(shí)例了疼蛾,而是將位于Activity B之上的Activity C肛跌、D移除出棧,并調(diào)用Activity B 實(shí)例的 onNewIntent()
方法据过,堆棧也就變成了 A-B惋砂。
singleInstance 模式
單一實(shí)例模式,這是一種加強(qiáng)的 singleTask 模式绳锅,它除了具有 singleTask 模式的所有特性外西饵,還加強(qiáng)了一點(diǎn),那就是具有此模式的Activity鳞芙,只能單獨(dú)的運(yùn)行在一個(gè)任務(wù)棧中眷柔,而且其他的Activity也不能運(yùn)行在它的棧中,即使是由它啟動(dòng)的Activity原朝。也就是說(shuō)驯嘱,一旦一個(gè)Activity設(shè)置了 singleInstance 模式,那么系統(tǒng)就會(huì)為它創(chuàng)建一個(gè)新的任務(wù)棧喳坠,然后它獨(dú)立的運(yùn)行在該棧中鞠评,同時(shí),由于棧內(nèi)復(fù)用特性壕鹉,后續(xù)在啟動(dòng)也不會(huì)在新建它的實(shí)例了剃幌,除非它已經(jīng)被系統(tǒng)銷(xiāo)毀了。
使用 Intent 標(biāo)志修改Activity的啟動(dòng)模式
啟動(dòng) Activity 時(shí)晾浴,可以通過(guò)在傳遞給 startActivity() 的 Intent 中加入相應(yīng)的標(biāo)志负乡,修改 Activity 的啟動(dòng)模式〖够耍可用于修改默認(rèn)行為的標(biāo)志包括:FLAG_ACTIVITY_NEW_TASK
抖棘、FLAG_ACTIVITY_SINGLE_TOP
、FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_NEW_TASK
在新任務(wù)棧中啟動(dòng) Activity。如果已經(jīng)存在 Activity 運(yùn)行任務(wù)棧切省,則該任務(wù)棧會(huì)轉(zhuǎn)到前臺(tái)并恢復(fù)其最后狀態(tài)最岗,同時(shí) Activity 會(huì)在 onNewIntent()
中收到新 Intent。與 "singleTask" launchMode 的行為相同朝捆。
FLAG_ACTIVITY_SINGLE_TOP
如果正在啟動(dòng)的 Activity 是當(dāng)前 Activity(位于任務(wù)棧的頂部)仑性,只是回調(diào)該Activity onNewIntent()
方法,而不是創(chuàng)建 Activity 的新實(shí)例右蹦。與 "singleTop" launchMode 的行為相同诊杆。
FLAG_ACTIVITY_CLEAR_TOP
使用這種標(biāo)記位是一般與 FLAG_ACTIVITY_NEW_TASK
或者 "singleTask" launchMode 一起使用。
- ① 被啟動(dòng)的Activity如果是
FLAG_ACTIVITY_NEW_TASK
或者 "singleTask" launchMode 模式何陆,那么啟動(dòng)時(shí)如果已經(jīng)存在該Activity的實(shí)例晨汹,就不會(huì)新創(chuàng)建了,而是移除它之上的所有Activity實(shí)例贷盲,并調(diào)用它的onNewIntent()
方法 - ② 被啟動(dòng)的Activity如果是默認(rèn)的啟動(dòng)模式(standard)淘这,那么啟動(dòng)它是將連同它自己以及它之上的Activity實(shí)例都要出棧,然后新建一個(gè)它的實(shí)例放入棧中巩剖。
處理關(guān)聯(lián):Activity 的 taskAffinity
屬性
taskAffinity
可以翻譯為任務(wù)相關(guān)性铝穷,這個(gè)屬性指明了Activity所需要的任務(wù)棧名稱(chēng)。默認(rèn)情況下佳魔,同一個(gè)應(yīng)用的所有Activity優(yōu)先位于相同的任務(wù)棧中曙聂,棧的名稱(chēng)為應(yīng)用的包名。當(dāng)然我們也可以為每個(gè)Activity指定taskAffinity
屬性鞠鲜,使他們運(yùn)行在不同的任務(wù)棧中宁脊,但是這個(gè)名稱(chēng)不能喝包名相同,否則相當(dāng)于沒(méi)有沒(méi)有指定贤姆。
重點(diǎn)注意:給taskAffinity
屬性的值是字符串形式榆苞,但是該值必須包含"."分隔符,否則報(bào)錯(cuò)霞捡,不能安裝坐漏。
taskAffinity
主要有兩種用法:①與 FLAG_ACTIVITY_NEW_TASK
/singleTask 模式配合使用;②與 Activity 的另一個(gè)屬性 allowTaskReparenting 配合使用碧信。其他的情況沒(méi)有意義赊琳。
與 FLAG_ACTIVITY_NEW_TASK
/singleTask 模式配合使用
默認(rèn)情況下,新 Activity 會(huì)啟動(dòng)到調(diào)用 startActivity() 的 Activity 任務(wù)棧中音婶。 但是慨畸,如果傳遞給 startActivity() 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK
標(biāo)志莱坎,則系統(tǒng)會(huì)尋找其他任務(wù)棧來(lái)儲(chǔ)存新的 Activity 實(shí)例衣式。這通常是新任務(wù)棧,但未做強(qiáng)制要求。 如果現(xiàn)有任務(wù)棧與新 Activity 所需要的任務(wù)棧(這個(gè)所需要的任務(wù)棧的名稱(chēng)可以在清單文件中通過(guò)activity標(biāo)簽的taskAffinity
屬性指定)具有相同名稱(chēng)碴卧,則會(huì)將 Activity 啟動(dòng)到該任務(wù)棧中弱卡。 否則,將創(chuàng)建新的任務(wù)棧住册,并將Activity實(shí)例放入棧中婶博。
與 Activity 的另一個(gè)屬性 allowTaskReparenting 配合使用
將一個(gè) Activity 的 allowTaskReparenting 屬性設(shè)置為 "true"。在這種情況下荧飞,Activity 可以從其啟動(dòng)的任務(wù)棧移動(dòng)到與其具有關(guān)聯(lián)的任務(wù)棧中(如果該任務(wù)出現(xiàn)在前臺(tái))凡人。
具體一點(diǎn):比如有2個(gè)應(yīng)用A和B,應(yīng)用A啟動(dòng)了應(yīng)用B的一個(gè)Activity C(Activity C的allowTaskReparenting
屬性值為 true
)叹阔,然后按Home鍵回到桌面挠轴,接著打開(kāi)應(yīng)用B,那么現(xiàn)在啟動(dòng)的并不是應(yīng)用B的主Activity耳幢,而是重新顯示B應(yīng)用的Activity C岸晦,或者說(shuō)是Activity C從應(yīng)用A的任務(wù)棧轉(zhuǎn)移到了應(yīng)用B的任務(wù)棧中了。
清理返回棧
如果用戶(hù)長(zhǎng)時(shí)間離開(kāi)任務(wù)睛藻,則系統(tǒng)會(huì)清除根 Activity以外的所有 Activity 的任務(wù)启上。 當(dāng)用戶(hù)再次返回到任務(wù)時(shí),僅恢復(fù)根 Activity店印。系統(tǒng)這樣做的原因是冈在,經(jīng)過(guò)很長(zhǎng)一段時(shí)間后,用戶(hù)可能已經(jīng)放棄之前執(zhí)行的操作按摘,返回到任務(wù)是要開(kāi)始執(zhí)行新的操作讥邻。但是可以使用下列幾個(gè) Activity 屬性修改此行為:alwaysRetainTaskState、clearTaskOnLaunch院峡、finishOnTaskLaunch兴使。
alwaysRetainTaskState
如果在任務(wù)棧的根 Activity 中將此屬性設(shè)置為 "true",則不會(huì)發(fā)生剛才所述的默認(rèn)行為照激。即使在很長(zhǎng)一段時(shí)間后发魄,任務(wù)棧仍將所有 Activity 實(shí)例保留在其堆棧中。此屬性只對(duì)task的根Activity起作用俩垃,其他的Activity都會(huì)被忽略
clearTaskOnLaunch
如果在任務(wù)棧的根 Activity 中將此屬性設(shè)置為 "true"励幼,則每當(dāng)用戶(hù)離開(kāi)任務(wù)棧然后返回時(shí),系統(tǒng)都會(huì)將堆棧清除到只剩下根 Activity口柳。 換而言之苹粟,它與 alwaysRetainTaskState 正好相反。即使只離開(kāi)任務(wù)棧片刻時(shí)間跃闹,重新打開(kāi)時(shí)也始終會(huì)返回到任務(wù)棧的初始狀態(tài)嵌削。此屬性只對(duì)task的根Activity起作用毛好,其他的Activity都會(huì)被忽略
finishOnTaskLaunch
此屬性類(lèi)似于 clearTaskOnLaunch,但是它對(duì)單個(gè) Activity 起作用苛秕,而非整個(gè)任務(wù)棧肌访。 此外,它還有可能會(huì)導(dǎo)致任何 Activity 停止艇劫,包括根 Activity吼驶。 設(shè)置為 "true" 時(shí),Activity 仍是任務(wù)棧的一部分店煞,但是僅限于當(dāng)前會(huì)話蟹演。如果用戶(hù)離開(kāi)當(dāng)前任務(wù)棧然后再返回任務(wù)棧,則Activity實(shí)例不復(fù)存在顷蟀。