Activity 啟動(dòng)模式及任務(wù)棧探究

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_TASKIntent.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_TOPFLAG_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ù)存在顷蟀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末轨帜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子衩椒,更是在濱河造成了極大的恐慌蚌父,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毛萌,死亡現(xiàn)場(chǎng)離奇詭異苟弛,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)阁将,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)膏秫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人做盅,你說(shuō)我怎么就攤上這事缤削。” “怎么了吹榴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵亭敢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我图筹,道長(zhǎng)帅刀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任远剩,我火速辦了婚禮扣溺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓜晤。我一直安慰自己锥余,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布痢掠。 她就那樣靜靜地躺著驱犹,像睡著了一般嘲恍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上着绷,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音锌云,去河邊找鬼荠医。 笑死,一個(gè)胖子當(dāng)著我的面吹牛桑涎,可吹牛的內(nèi)容都是我干的彬向。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼攻冷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼娃胆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起等曼,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤里烦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后禁谦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胁黑,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年州泊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丧蘸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡遥皂,死狀恐怖力喷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情演训,我是刑警寧澤弟孟,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站样悟,受9級(jí)特大地震影響披蕉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乌奇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一没讲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧礁苗,春花似錦爬凑、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)于样。三九已至,卻和暖如春潘靖,著一層夾襖步出監(jiān)牢的瞬間穿剖,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工卦溢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糊余,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓单寂,卻偏偏與公主長(zhǎng)得像贬芥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宣决,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容