徹底弄懂Activity的啟動(dòng)模式和任務(wù)棧

轉(zhuǎn)載自:https://blog.csdn.net/yh_coco/article/details/79427595

Activity的啟動(dòng)模式和任務(wù)棧是Android的重點(diǎn)和難點(diǎn),也是Android面試的炒奖妫考必考知識(shí)點(diǎn)馁龟,而很多同學(xué)即使是搞Android開(kāi)發(fā)好幾年的同學(xué)也不一定完全搞懂了窿冯,可能也是一知半解。本文讓你徹底搞懂Android中Activity的啟動(dòng)模式和任務(wù)棧销凑!

前言

一個(gè)應(yīng)用程序當(dāng)中通常都會(huì)包含很多個(gè)Activity焙贷,每個(gè)Activity都是一個(gè)具有特定的功能缩抡,并且可以讓用戶進(jìn)行操作的組件。另外哗讥,Activity之間可以相互啟動(dòng),當(dāng)前應(yīng)用的Activity甚至可以去啟動(dòng)其他應(yīng)用的Activity胞枕。

比如你的應(yīng)用希望去發(fā)送一封郵件杆煞,你就可以定義一個(gè)具有”send”動(dòng)作的Intent,并且傳入一些數(shù)據(jù)腐泻,如對(duì)方郵箱地址决乎、郵件內(nèi)容等。這樣派桩,如果另外一個(gè)應(yīng)用程序中的某個(gè)Activity聲明自己是可以響應(yīng)這種Intent的构诚,那么這個(gè)Activity就會(huì)被打開(kāi)。當(dāng)郵件發(fā)送之后铆惑,按下返回鍵仍然還是會(huì)回到你的應(yīng)用程序當(dāng)中范嘱,這讓用戶看起來(lái)好像剛才那個(gè)編寫郵件的Activity就是你的應(yīng)用程序當(dāng)中的一部分。

所以說(shuō)员魏,即使有很多個(gè)Activity分別都是來(lái)自于不同應(yīng)用程序的丑蛤,Android系統(tǒng)仍然可以將它們無(wú)縫地結(jié)合到一起。

那這一切是怎么實(shí)現(xiàn)的呢撕阎?這就要講到本文要介紹的Activity任務(wù)棧以及Activity啟動(dòng)模式了受裹。

任務(wù)棧是什么

任務(wù)棧Task,是一種用來(lái)放置Activity實(shí)例的容器虏束,他是以棧的形式進(jìn)行盛放棉饶,也就是所謂的先進(jìn)后出,主要有2個(gè)基本操作:壓棧和出棧镇匀,其所存放的Activity是不支持重新排序的照藻,只能根據(jù)壓棧和出棧操作更改Activity的順序。

啟動(dòng)一個(gè)Application的時(shí)候坑律,系統(tǒng)會(huì)為它默認(rèn)創(chuàng)建一個(gè)對(duì)應(yīng)的Task岩梳,用來(lái)放置根Activity。默認(rèn)啟動(dòng)Activity會(huì)放在同一個(gè)Task中晃择,新啟動(dòng)的Activity會(huì)被壓入啟動(dòng)它的那個(gè)Activity的棧中冀值,并且顯示它。當(dāng)用戶按下回退鍵時(shí)宫屠,這個(gè)Activity就會(huì)被彈出棧列疗,按下Home鍵回到桌面,再啟動(dòng)另一個(gè)應(yīng)用浪蹂,這時(shí)候之前那個(gè)Task就被移到后臺(tái)抵栈,成為后臺(tái)任務(wù)棧告材,而剛啟動(dòng)的那個(gè)Task就被調(diào)到前臺(tái),成為前臺(tái)任務(wù)棧古劲,Android系統(tǒng)顯示的就是前臺(tái)任務(wù)棧中的Top實(shí)例Activity斥赋。

任務(wù)棧的作用

以往基于應(yīng)用(application)的程序開(kāi)發(fā)中,程序具有明確的邊界产艾,一個(gè)程序就是一個(gè)應(yīng)用疤剑,一個(gè)應(yīng)用為了實(shí)現(xiàn)功能可以采用開(kāi)辟新線程甚至新進(jìn)程來(lái)輔助,但是應(yīng)用與應(yīng)用之間不能復(fù)用資源和功能闷堡。

而Android引入了基于組件開(kāi)發(fā)的軟件架構(gòu)隘膘,雖然我們開(kāi)發(fā)android程序,仍然使用一個(gè)apk工程一個(gè)Application的開(kāi)發(fā)形式杠览,但是對(duì)于Aplication的開(kāi)發(fā)就用到了Activity弯菊、service等四大組件,其中的每一個(gè)組件踱阿,都是可以被跨應(yīng)用復(fù)用的管钳,這就是android的神奇之處。

雖然組件可以跨應(yīng)用被調(diào)用扫茅,但是一個(gè)組件所在的進(jìn)程必須是在組件所在的Aplication進(jìn)程中蹋嵌。由于android強(qiáng)化了組件概念,弱化了Aplication的概念葫隙,所以在android程序開(kāi)發(fā)中栽烂,A應(yīng)用的A組件想要使用拍照或錄像的功能就可以不用去針對(duì)Camera類進(jìn)行開(kāi)發(fā),直接調(diào)用系統(tǒng)自帶的攝像頭應(yīng)用(稱其B應(yīng)用)中的組件(稱其B組件)就可以了恋脚,

但是這就引發(fā)了一個(gè)新問(wèn)題腺办,A組件運(yùn)行在A應(yīng)用中,B組件運(yùn)行在B應(yīng)用中糟描,自然都不在同一個(gè)進(jìn)程中怀喉,那么從B組件中返回的時(shí)候,如何實(shí)現(xiàn)正確返回到A組件呢船响?Task就是來(lái)負(fù)責(zé)實(shí)現(xiàn)這個(gè)功能的躬拢,它是從用戶角度來(lái)理解應(yīng)用而建立的一個(gè)抽象概念。因?yàn)橛脩羲芸吹降慕M件就是Activity见间,所以Task可以理解為實(shí)現(xiàn)一個(gè)功能而負(fù)責(zé)管理所有用到的Activity實(shí)例的棧聊闯。

棧是一個(gè)先進(jìn)后出的線性表,根據(jù)Activity在當(dāng)前棧結(jié)構(gòu)中的位置米诉,來(lái)決定該Activity的狀態(tài)菱蔬。正常情況下,當(dāng)一個(gè)Activity啟動(dòng)了另一個(gè)Activity的時(shí)候,新啟動(dòng)的Activity就會(huì)置于任務(wù)棧的頂端拴泌,并處于活動(dòng)狀態(tài)魏身,而啟動(dòng)它的Activity雖然成功身退,但依然保留在任務(wù)棧中蚪腐,處于停止?fàn)顟B(tài)箭昵,當(dāng)用戶按下返回鍵或者調(diào)用finish()方法時(shí)削茁,系統(tǒng)會(huì)移除頂部Activity茧跋,讓后面的Activity恢復(fù)活動(dòng)狀態(tài)瘾杭。當(dāng)然粥烁,世界不可能一直這么“和諧”讨阻,可以給Activity設(shè)置一些“特權(quán)”钝吮,來(lái)打破這種“和諧”的模式奇瘦,這種特權(quán)耳标,就是通過(guò)在AndroidManifest文件中的屬性andorid:launchMode來(lái)設(shè)置或者通過(guò)Intent的flag來(lái)設(shè)置的次坡,下面就先介紹下Activity的幾種啟動(dòng)模式画畅。

standard

默認(rèn)模式夜赵,可以不用寫配置。在這個(gè)模式下寇僧,都會(huì)默認(rèn)創(chuàng)建一個(gè)新的實(shí)例。因此视粮,在這種模式下蕾殴,可以有多個(gè)相同的實(shí)例,也允許多個(gè)相同Activity疊加荡灾。應(yīng)用場(chǎng)景:絕大多數(shù)Activity批幌。

如果以這種方式啟動(dòng)的Activity被跨進(jìn)程調(diào)用,在5.0之前新啟動(dòng)的Activity實(shí)例會(huì)放入發(fā)送Intent的Task的棧的頂部截粗,盡管它們屬于不同的程序恢着,這似乎有點(diǎn)費(fèi)解看起來(lái)也不是那么合理桐愉,所以在5.0之后掰派,上述情景會(huì)創(chuàng)建一個(gè)新的Task,新啟動(dòng)的Activity就會(huì)放入剛創(chuàng)建的Task中靡羡,這樣就合理的多了。

singleTop

棧頂復(fù)用模式略步,如果要開(kāi)啟的activity在任務(wù)棧的頂部已經(jīng)存在描扯,就不會(huì)創(chuàng)建新的實(shí)例,而是調(diào)用 onNewIntent() 方法趟薄。避免棧頂?shù)腶ctivity被重復(fù)的創(chuàng)建。應(yīng)用場(chǎng)景:在通知欄點(diǎn)擊收到的通知,然后需要啟動(dòng)一個(gè)Activity卒落,這個(gè)Activity就可以用singleTop儡毕,否則每次點(diǎn)擊都會(huì)新建一個(gè)Activity。當(dāng)然實(shí)際的開(kāi)發(fā)過(guò)程中疆股,測(cè)試妹紙沒(méi)準(zhǔn)給你提過(guò)這樣的bug:某個(gè)場(chǎng)景下連續(xù)快速點(diǎn)擊葵萎,啟動(dòng)了兩個(gè)Activity。如果這個(gè)時(shí)候待啟動(dòng)的Activity使用 singleTop模式也是可以避免這個(gè)Bug的唱凯。

同standard模式,如果是外部程序啟動(dòng)singleTop的Activity谎痢,在Android 5.0之前新創(chuàng)建的Activity會(huì)位于調(diào)用者的Task中磕昼,5.0及以后會(huì)放入新的Task中。

singleTask

棧內(nèi)復(fù)用模式节猿, activity只會(huì)在任務(wù)棧里面存在一個(gè)實(shí)例票从。如果要激活的activity,在任務(wù)棧里面已經(jīng)存在滨嘱,就不會(huì)創(chuàng)建新的activity峰鄙,而是復(fù)用這個(gè)已經(jīng)存在的activity,調(diào)用 onNewIntent() 方法太雨,并且清空這個(gè)activity任務(wù)棧上面所有的activity吟榴。應(yīng)用場(chǎng)景:大多數(shù)App的主頁(yè)。對(duì)于大部分應(yīng)用囊扳,當(dāng)我們?cè)谥鹘缑纥c(diǎn)擊回退按鈕的時(shí)候都是退出應(yīng)用吩翻,那么當(dāng)我們第一次進(jìn)入主界面之后,主界面位于棧底锥咸,以后不管我們打開(kāi)了多少個(gè)Activity狭瞎,只要我們?cè)俅位氐街鹘缑妫紤?yīng)該使用將主界面Activity上所有的Activity移除的方式來(lái)讓主界面Activity處于棧頂搏予,而不是往棧頂新加一個(gè)主界面Activity的實(shí)例熊锭,通過(guò)這種方式能夠保證退出應(yīng)用時(shí)所有的Activity都能報(bào)銷毀。

在跨應(yīng)用Intent傳遞時(shí),如果系統(tǒng)中不存在singleTask Activity的實(shí)例碗殷,那么將創(chuàng)建一個(gè)新的Task精绎,然后創(chuàng)建SingleTask Activity的實(shí)例,將其放入新的Task中亿扁。

假如目前有個(gè)任務(wù)棧T1中的情況是ABC捺典,這個(gè)時(shí)候ActivityD以singleTask模式請(qǐng)求啟動(dòng),其所需要的任務(wù)棧正是T1从祝,則系統(tǒng)會(huì)直接創(chuàng)建D的實(shí)例并將其入棧到T1中襟己。?

假如DActivity啟動(dòng)所需要的任務(wù)棧為T2,由于T2和D的實(shí)例均不存在,那么系統(tǒng)會(huì)先創(chuàng)建任務(wù)棧T2牍陌,然后再創(chuàng)建D的實(shí)例并將其入棧到T2中擎浴。我們可以通過(guò)設(shè)置Activity的taskAffinity屬性來(lái)模擬這一場(chǎng)景。

1

如果D所需的任務(wù)棧為T3毒涧,并且當(dāng)前任務(wù)棧T3的情況為ADBC贮预,根據(jù)棧內(nèi)復(fù)用的原則,此時(shí)D不會(huì)重新創(chuàng)建契讲,系統(tǒng)會(huì)把D切換到棧頂并調(diào)用其onNewIntent()方法仿吞,同時(shí)由于singleTask默認(rèn)具有ClearTop的效果,會(huì)導(dǎo)致棧內(nèi)所有在D上面的Activity全部出棧捡偏,于是最終T3的情況為AD唤冈。?

假如目前有兩個(gè)任務(wù)棧,前臺(tái)任務(wù)棧T4的情況為AB,后臺(tái)任務(wù)棧t4里存有CD,假設(shè)CD的啟動(dòng)模式均為singleTask银伟,現(xiàn)在由B去啟動(dòng)D,那么整個(gè)后臺(tái)任務(wù)都會(huì)被切換到前臺(tái)你虹,這個(gè)時(shí)候整個(gè)棧就變成了ABCD。

假如上面的其他條件不變彤避,B啟動(dòng)的是C而不是D,那么整個(gè)棧的情況就變成了ABC,因?yàn)镈在C上面傅物,會(huì)被清理出棧。?

singleInstance

單一實(shí)例模式琉预,整個(gè)手機(jī)操作系統(tǒng)里面只有一個(gè)實(shí)例存在董饰。不同的應(yīng)用去打開(kāi)這個(gè)activity 共享公用的同一個(gè)activity。他會(huì)運(yùn)行在自己?jiǎn)为?dú)模孩,獨(dú)立的任務(wù)棧里面尖阔,并且任務(wù)棧里面只有他一個(gè)實(shí)例存在。應(yīng)用場(chǎng)景:呼叫來(lái)電界面榨咐。這種模式的使用情況比較罕見(jiàn)介却,在Launcher中可能使用】樽拢或者你確定你需要使Activity只有一個(gè)實(shí)例齿坷。建議謹(jǐn)慎使用桂肌。

設(shè)置Intent的Flag

系統(tǒng)提供了兩種方式來(lái)設(shè)置一個(gè)Activity的啟動(dòng)模式,除了在AndroidManifest文件中設(shè)置以外永淌,還可以通過(guò)Intent的Flag來(lái)設(shè)置一個(gè)Activity的啟動(dòng)模式崎场,下面我們?cè)诤?jiǎn)單介紹下一些Flag。

FLAG_ACTIVITY_NEW_TASK

使用一個(gè)新的Task來(lái)啟動(dòng)一個(gè)Activity遂蛀,但啟動(dòng)的每個(gè)Activity都講在一個(gè)新的Task中谭跨。該Flag通常使用在從Service中啟動(dòng)Activity的場(chǎng)景,由于Service中并不存在Activity棧李滴,所以使用該Flag來(lái)創(chuàng)建一個(gè)新的Activity棧螃宙,并創(chuàng)建新的Activity實(shí)例。

FLAG_ACTIVITY_SINGLE_TOP

使用singletop模式啟動(dòng)一個(gè)Activity所坯,與指定android:launchMode=“singleTop”效果相同谆扎。

FLAG_ACTIVITY_CLEAR_TOP

使用SingleTask模式來(lái)啟動(dòng)一個(gè)Activity,與指定android:launchMode=“singleTask”效果相同芹助。

FLAG_ACTIVITY_NO_HISTORY

Activity使用這種模式啟動(dòng)Activity堂湖,當(dāng)該Activity啟動(dòng)其他Activity后,該Activity就消失了状土,不會(huì)保留在Activity棧中无蜂。

LaunchMode與StartActivityForResult

我們?cè)陂_(kāi)發(fā)過(guò)程中經(jīng)常會(huì)用到StartActivityForResult方法啟動(dòng)一個(gè)Activity,然后在onActivityResult()方法中可以接收到上個(gè)頁(yè)面的回傳值蒙谓,但你有可能遇到過(guò)拿不到返回值的情況酱讶,那有可能是因?yàn)锳ctivity的LaunchMode設(shè)置為了singleTask。5.0之后彼乌,android的LaunchMode與StartActivityForResult的關(guān)系發(fā)生了一些改變。兩個(gè)Activity渊迁,A和B慰照,現(xiàn)在由A頁(yè)面跳轉(zhuǎn)到B頁(yè)面,看一下LaunchMode與StartActivityForResult之間的關(guān)系:

這是為什么呢琉朽?

這是因?yàn)锳ctivityStackSupervisor類中的startActivityUncheckedLocked方法在5.0中進(jìn)行了修改毒租。在5.0之前,當(dāng)啟動(dòng)一個(gè)Activity時(shí)箱叁,系統(tǒng)將首先檢查Activity的launchMode墅垮,如果為A頁(yè)面設(shè)置為SingleInstance或者B頁(yè)面設(shè)置為singleTask或者singleInstance,則會(huì)在LaunchFlags中加入FLAG_ACTIVITY_NEW_TASK標(biāo)志,而如果含有FLAG_ACTIVITY_NEW_TASK標(biāo)志的話耕漱,onActivityResult將會(huì)立即接收到一個(gè)cancle的信息算色,而5.0之后這個(gè)方法做了修改,修改之后即便啟動(dòng)的頁(yè)面設(shè)置launchMode為singleTask或singleInstance螟够,onActivityResult依舊可以正常工作灾梦,也就是說(shuō)無(wú)論設(shè)置哪種啟動(dòng)方式峡钓,StartActivityForResult和onActivityResult()這一組合都是有效的。所以如果你目前正好基于5.0做相關(guān)開(kāi)發(fā)若河,不要忘了向下兼容能岩,這里有個(gè)坑請(qǐng)注意避讓。

總結(jié)

實(shí)際開(kāi)發(fā)過(guò)程中如果采用比較合理的Activity啟動(dòng)模式來(lái)做好任務(wù)棧的管理萧福,可以事半功倍拉鹃。在launchMode的選擇上首先要搞清楚當(dāng)前的Activity的作用,以及實(shí)際使用場(chǎng)景來(lái)做出合理選擇鲫忍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末膏燕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子饲窿,更是在濱河造成了極大的恐慌煌寇,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逾雄,死亡現(xiàn)場(chǎng)離奇詭異阀溶,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)鸦泳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門银锻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人做鹰,你說(shuō)我怎么就攤上這事击纬。” “怎么了钾麸?”我有些...
    開(kāi)封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵更振,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我饭尝,道長(zhǎng)肯腕,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任钥平,我火速辦了婚禮实撒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涉瘾。我一直安慰自己知态,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布立叛。 她就那樣靜靜地躺著负敏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秘蛇。 梳的紋絲不亂的頭發(fā)上原在,一...
    開(kāi)封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天友扰,我揣著相機(jī)與錄音,去河邊找鬼庶柿。 笑死村怪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浮庐。 我是一名探鬼主播甚负,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼审残!你這毒婦竟也來(lái)了梭域?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤搅轿,失蹤者是張志新(化名)和其女友劉穎病涨,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體璧坟,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡既穆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雀鹃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幻工。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖黎茎,靈堂內(nèi)的尸體忽然破棺而出囊颅,到底是詐尸還是另有隱情,我是刑警寧澤傅瞻,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布踢代,位于F島的核電站,受9級(jí)特大地震影響嗅骄,放射性物質(zhì)發(fā)生泄漏奸鬓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一掸读、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宏多,春花似錦儿惫、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至更胖,卻和暖如春铛铁,著一層夾襖步出監(jiān)牢的瞬間隔显,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工饵逐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留括眠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓倍权,卻偏偏與公主長(zhǎng)得像掷豺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子薄声,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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