Activity的4種啟動模式

今天想來說說Android的啟動模式薪铜,一來自己做一個總結(jié),二來如果能幫助到別人就更好了~

首先來看一個實際的業(yè)務(wù)場景恩溅。我之前在公司實習(xí)的時候隔箍,我所在的部門只負責(zé)一個品類(國際機票),那用戶從機票首頁開始搜索機票到最終完成訂單并支付大致分為以下幾個流程(實際流程因為考慮的問題比較多脚乡,所以要比這稍微復(fù)雜些):
  1. 用戶在機票首頁確認好出發(fā)蜒滩、到達目的地、日期及人數(shù)后奶稠,就可以點擊“搜索”進入到搜索結(jié)果頁
  2. 用戶根據(jù)意愿選擇相應(yīng)航班并點擊進入確認訂單頁
  3. 用戶確認好訂單內(nèi)容點擊下單俯艰,開始支付,支付完成跳支付完成頁
  4. 用戶可以選擇回到首頁锌订,或者去公共訂單系統(tǒng)查看訂單詳情(這屬于公共部分蟆炊,不在我們討論的范圍內(nèi)了)

經(jīng)過上面4個步驟整個購票流程就算結(jié)束了。但是瀑志,這里有個問題涩搓,就是用戶從首頁到最后的支付完成頁之間經(jīng)歷了這么多的Activity,那用戶完成了支付后想回到首頁是不是要一層一層的往回退才行劈猪?如果是這樣的話昧甘,那就太不人性化了,那怎樣才能在完成支付后一鍵回到首頁呢战得?有人可能會說充边,那就直接從支付完成頁跳轉(zhuǎn)到首頁不就可以了嗎?這樣當(dāng)然是可以的常侦,但是這樣做那之前打開的那么多Activity怎么辦呢浇冰?難道就讓它們待在Activity任務(wù)棧中嗎?這樣豈不是太浪費內(nèi)存了聋亡!請看我畫的示意圖:

  

如上圖所示肘习,如果我們在支付完成之后啟動首頁Activity那頁面1-頁面5之間的所有Activity對用戶來說都是無用的,并且占用的內(nèi)存坡倔,極大地浪費這本來可能就很緊張的Android內(nèi)存資源漂佩。那有沒有一些優(yōu)雅的方式來解決這個問題呢矩父? 我們可以想象一下催跪,有沒有可能在從頁面4跳轉(zhuǎn)首頁的時候把原來處于首頁之上的所有Activity全部干掉呢?這樣不就剛好解決了我們剛剛所說浪費資源的問題了嗎心剥?就像下圖一樣:

很顯然征堪,無論從用戶體驗的角度還是從內(nèi)存優(yōu)化的角度來看第二種方式都是最為優(yōu)雅的瘩缆。

那怎么實現(xiàn)這種需求呢?有人可能會說佃蚜,可以將任務(wù)棧中頁面1之上的所有Activity一層一層地執(zhí)行finish()方法銷毀掉庸娱,這樣就可以回退到頁面1着绊。這樣確實是可行的,但是有個問題:如果希望從頁面4跳轉(zhuǎn)到頁面1時傳遞一些數(shù)據(jù)回去涌韩,比如說訂單號畔柔、支付完成等信息氯夷,還需要在finish()執(zhí)行前執(zhí)行setResult(int resultCode, Intent intent)將要往回傳遞的數(shù)據(jù)放在Intent里面臣樱。

這樣做其實是有風(fēng)險的,因為Android系統(tǒng)并不保證Activity任務(wù)棧中那些不可見的Activity的狀態(tài)是一直被保存著的腮考,如果出現(xiàn)系統(tǒng)內(nèi)存不足的情況雇毫,Android系統(tǒng)是可以回收那些處于不可見狀態(tài)的Activity的。也就是說踩蔚,一旦中間有一Activity被銷毀了棚放,那這個傳遞鏈就失去功效了。

那應(yīng)該怎么辦呢馅闽?別著急飘蚯,Google的Android工程師早就已經(jīng)為我們想到這種需求了。那應(yīng)該怎么做呢福也?其實就是通過設(shè)置Activity的啟動模式來實現(xiàn)局骤。好,問題拋出了暴凑,下面讓我們一步一步來看峦甩。

Activity有哪幾種啟動模式?

Activity一共有4種啟動模式现喳,分別是:

  • standard
  • singleTop
  • singleTask
  • singleInstance

下面我來分別做介紹凯傲。

一、standard

顧名思義嗦篱,standard英文意思就是“標(biāo)準(zhǔn)的”冰单。

也就是說這種啟動模式是默認的,我們平時在開發(fā)中使用最多的就是Standard模式的灸促。

如果一個Activity的啟動模式被設(shè)置成standard球凰,那么它可以無限制的創(chuàng)建。你每一次通過Intent去啟動這種模式的Activity都會重新創(chuàng)建一個腿宰。

大家可以想象一下郵箱里的收件箱(假設(shè)我們將打開郵件的Activity的啟動模式設(shè)置為Standard呕诉,當(dāng)然這也是默認的模式)里有10封郵件。我們給查看郵件的Activity起名為CheckEmailActivity,我點擊第一封郵件將會打開一個CheckEmailActivity吃度,當(dāng)我看完之后點擊下一封郵件甩挫,另一個CheckEmailActivity又會被創(chuàng)建,這樣如果我們將10封郵件全部看完椿每,那在Activity任務(wù)棧中將會有10個CheckEmailActivity伊者,而且如果我想回到收件箱頁面還必須點10次返回鍵英遭!想想是不是很可怕?

所以說standard模式雖然很常用亦渗,但也不是適用于任何場合挖诸。
  
另外說一點,standard模式在Android 5.0(Lollipop)之前和之后是有區(qū)別的法精。
  
** Android Lollipop之前**

standard模式的Activity總是會被創(chuàng)建在啟動它的Activity同一個任務(wù)棧中頂端(任務(wù)棧是一個棧結(jié)構(gòu)多律,先進后出 First In Last Out),就算他們來自不同的應(yīng)用搂蜓。
  
想象一個場景狼荞,如果你在A應(yīng)用中要分享一個本地圖片,這樣會打開系統(tǒng)的圖片查看應(yīng)用中的圖片選擇器Activity帮碰,雖然這兩個Activity來自不同的應(yīng)用相味,但Android系統(tǒng)仍將會把他們放在同一個任務(wù)棧中,即A應(yīng)用的任務(wù)棧中殉挽。
  


Android Lollipop之后

如果將要啟動的Activity和啟動它的Activity來自同一個應(yīng)用丰涉,那沒話說,和Lollipop之前一樣斯碌,新的Activity會被創(chuàng)建在當(dāng)前任務(wù)棧中的頂端一死。

但是如果它們來自不同的應(yīng)用,那就會創(chuàng)建一個新的任務(wù)棧输拇,再把要啟動的Activity放在新的任務(wù)棧中摘符,這時這個新啟動的Activity就是新創(chuàng)建的任務(wù)站點的根Activity。如下圖所示:

  

二策吠、singleTop

顧名思義逛裤,singleTop的意思就是“在頂部只能有一個”。

這種啟動模式非常類似于standard猴抹,但是也有一些 區(qū)別
  
如果在啟動這種模式的Activity的時候带族,當(dāng)前任務(wù)棧的頂端已經(jīng)存在了相同的Activity,那系統(tǒng)就不會再創(chuàng)建新的蟀给,而是回調(diào)任務(wù)棧中已經(jīng)存在的該Activity的onNewIntent( )方法蝙砌。請看下面的示意圖:
  


  
也正因為SingleTop啟動模式的特殊性,所以在開發(fā)時跋理,如果指定了一個Activity的啟動模式是singleTop的那就應(yīng)該既要重寫onCreated()方法用于應(yīng)對第一次創(chuàng)建的情況择克,也要重寫onNewIntent( )方法來應(yīng)對重復(fù)創(chuàng)建的情況

其實大家可以想象一下前普,這種啟動模式的應(yīng)用場景肚邢。Android既然提供了這種啟動模式,說明肯定有應(yīng)有場景需要這樣的方式。其實最常用的場景就是搜索骡湖,比方說我們在搜索框中輸入想要搜索的內(nèi)容點擊搜索進入SearchResultActivty(搜索結(jié)果頁)查看搜索的結(jié)果(一般我們也會在搜索結(jié)果頁提供搜索框贱纠,這樣用戶無需點擊返回鍵回到上一個頁面再在搜索框中輸入搜索內(nèi)容點擊搜索),如果此時用戶還想搜點別的東西响蕴,就可以直接在當(dāng)前的搜索結(jié)果頁SearchResultActivty中的搜索框輸入搜索內(nèi)容繼續(xù)搜索谆焊。

大家想象一下,如果我們把SearchResultActivty的啟動模式設(shè)置為Standard的話會是什么樣的景象浦夷。比如我們連著搜了10個內(nèi)容辖试,那就會啟動10個不同的SearchResultActivty,然而這些SearchResultActivty功能完全一樣军拟,完全沒有必要創(chuàng)建這么多剃执,而且還有一個和上一節(jié)中的郵箱一樣的問題誓禁,就是用戶搜索結(jié)束想回到首頁懈息,那就還得按10次返回鍵才能回到首頁,- -摹恰!

這時辫继,singleTop啟動模式就派上用場了,我們首先把SearchResultActivty的啟動模式設(shè)置為singleTop俗慈,這樣用戶在SearchResultActivty頁面中繼續(xù)搜索的時候姑宽,我們只需把用戶要搜索的內(nèi)容放在Intent里面然后啟動SearchResultActivty,這時系統(tǒng)并不會重新創(chuàng)建新的SearchResultActivty闺阱,而是回調(diào)當(dāng)前任務(wù)棧棧頂?shù)腟earchResultActivty的onNewIntent()方法來接收帶有用戶搜索內(nèi)容信息的Intent炮车,然后我們拿到用戶搜索內(nèi)容后調(diào)搜索接口,并根據(jù)接口返回內(nèi)容重新刷新布局即可酣溃,似不似很神奇瘦穆?其實我們在上一節(jié)提到的郵箱的問題,也是用這種方式來解決的赊豌,原理和搜索一樣的扛或。

三、singleTask

這種啟動模式的Activity在Android系統(tǒng)中只允許存在一個實例碘饼。

如果系統(tǒng)中已經(jīng)存在了該種啟動模式的目標(biāo)Activity熙兔,則系統(tǒng)并不會重新創(chuàng)建一個目標(biāo)Activity,而是首先將持有目標(biāo)Activity的整個任務(wù)棧都會被置于前臺(用戶可見)艾恼,并且通過onNewIntent( )方法將啟動目標(biāo)Activity的Intent傳遞給目標(biāo)Activity住涉,置于目標(biāo)Activity拿到這個Intent之后要做什么操作,系統(tǒng)就不管了钠绍,隨便你拿來干什么舆声,哼~。

但是這里有個問題五慈,就是目標(biāo)Activity和源Activity是不是來自同一應(yīng)用纳寂。

源Activity和目標(biāo)Activity來自同一個應(yīng)用

這種情況還要分兩種情況說:

當(dāng)前系統(tǒng)中還沒有目標(biāo)Activity的實例
這種情況最簡單主穗,直接在當(dāng)前的任務(wù)棧中創(chuàng)建SingleTask模式的Activity并置于棧頂即可。

當(dāng)前系統(tǒng)中已經(jīng)存在目標(biāo)Activity的實例
這種情況比較特殊毙芜,因為系統(tǒng)會把任務(wù)棧中目標(biāo)Activity之上的所有Activity銷毀忽媒,以讓目標(biāo)Activity處在棧頂?shù)奈恢谩?/p>

這里還要還要再提醒大家的是,因為目標(biāo)Activity已經(jīng)存在腋粥,系統(tǒng)不會重新創(chuàng)建晦雨,而是通過onNewIntent()的方式把Intent傳遞過來,這點和singleTop模式有些類似隘冲。注意了闹瞧,這里讓我們回想一下文章開頭的我所說的場景,如何讓用戶在支付完成頁直接跳轉(zhuǎn)到首頁展辞,并把不需要的Activity銷毀奥邮?SingleTask啟動模式是不是剛好和我們的需求一致?請看下面的示意圖:

  
源Activity和目標(biāo)Activity來自不同應(yīng)用

這種情況也要分兩種情況說:

當(dāng)前系統(tǒng)中還沒有目標(biāo)Activity的實例
這時系統(tǒng)首先會看任務(wù)管理器中是否有目標(biāo)Actvity所在應(yīng)用的任務(wù)棧罗珍?如果有的話洽腺,那就直接在目標(biāo)Activity所在應(yīng)用的任務(wù)棧的棧頂創(chuàng)建即可。

如果任務(wù)管理器中沒有目標(biāo)Activity所在應(yīng)用的任務(wù)棧覆旱,系統(tǒng)就會創(chuàng)建其所在應(yīng)用的任務(wù)棧和目標(biāo)Activity蘸朋,并且把目標(biāo)Activity作為新建任務(wù)棧的根Activity。如下圖所示:

  

** 當(dāng)前系統(tǒng)中已經(jīng)存在目標(biāo)Activity的實例**
目標(biāo)Activity所在任務(wù)棧會被置于前臺(即用戶可見)扣唱,而且也會把目標(biāo)Activity之上的所有Actvity全部銷毀藕坯。

四、singleInstance

這種啟動模式和singleTask幾乎一樣噪沙,它也只允許系統(tǒng)中存在一個目標(biāo)Activity炼彪,包括上面我們所說的SingleTask的一些特性singleInstance都有。唯一不同的是曲聂,持有目標(biāo)Activity的任務(wù)棧中只能有目標(biāo)Activity一個Actvitiy霹购,不能再有別的Activity,對朋腋! 就是承包了這個任務(wù)棧齐疙!哈哈~

其實從這種啟動模式的名字也可以看出來它表示的意思旭咽,singleInstance直譯過來就是“單一實例”贞奋,什么意思呢?這話啊有兩層意思穷绵,我來給你分析分析:1. 跟系統(tǒng)說轿塔,“我是獨一無二的,不許和我一樣的人存在!”勾缭,這就是說系統(tǒng)中存在一個目標(biāo)Activity揍障。;2. 跟任務(wù)棧說俩由,“我是獨一無二的毒嫡,不許你心里再裝別的人!”幻梯,這就是說持有目標(biāo)Activity的任務(wù)棧中只能有目標(biāo)Activity一個Activity兜畸。這樣說是不是好理解一些,哈哈~
  
所以碘梢,如果要啟動singleInstance模式的Activity,那只能新創(chuàng)建一個任務(wù)棧用來放它咬摇,因為人家說了,“我是獨一無二的煞躬!”肛鹏。同樣的,如果從這種啟動模式的Activity中啟動別的Activity汰翠,那不好意思龄坪,我不管你是不是和我處在同一個應(yīng)用昭雌,我所在的任務(wù)棧只能擁有我一個人佛纫,您吶呈宇,另外讓系統(tǒng)給你創(chuàng)建一個任務(wù)棧待著去吧。

好了蜈漓,至此我們介紹了Activity的4種啟動模式了融虽,也大致了解了每種啟動模式的特點了有额,那接下里的問題就是怎么使用呢?問題又拋出來了茴迁,好萤衰,讓我們接著往下看。

怎么使用啟動模式胳螟?

有兩種方式來使用或者說設(shè)置Activity的啟動模式:

方式1:在AndroidMenifest.xml文件中設(shè)置
  


看到?jīng)]有糖耸,在<activity>標(biāo)簽中設(shè)置android:launchMode="****"屬性即可丘薛,****即我們上面所說的四種啟動模式。

方式2:通過為Intent添加標(biāo)識來設(shè)置
  


看到?jīng)]有舍扰,這里使用Intent的addFlags()方法來添加一些標(biāo)志边苹,其實這個addFlags()不光可以用來設(shè)置Activity的啟動模式裁僧,還能做很多事情聊疲,它的作用是給Intent添加一些附加屬性获洲。具體的可以參見Android api哈~
  
那我們想設(shè)置Activity的啟動模式應(yīng)該給addFlags()方法設(shè)置哪些參數(shù)呢贡珊?來飞崖,接著往下看:

  • FLAG_ACTIVITY_NEW_TASK
    與"singleTask"啟動模式的作用一樣。

  • FLAG_ACTIVITY_SINGLE_TOP
    與"singleTop"啟動模式的作用一樣蒜鸡。

  • FLAG_ACTIVITY_CLEAR_TOP
    這個標(biāo)識的意思比較特殊。它不對應(yīng)于我們上面所說的啟動模式中的任何一種叶沛,我們來看一下android api中對這個標(biāo)識的說明:

“如果正在啟動的 Activity 已在當(dāng)前任務(wù)中運行忘朝,則會銷毀當(dāng)前任務(wù)頂部的所有 Activity局嘁,并通過onNewIntent()
將此 Intent 傳遞給 Activity 已恢復(fù)的實例(現(xiàn)在位于頂部),而不是啟動該 Activity 的新實例悦昵。”

大家可能會發(fā)現(xiàn)但指,通過addFlags()的方式來設(shè)置啟動模式有局限性寡痰,只能顯示的設(shè)置“singleTask”和“singleTop”兩種啟動模式,而并沒有對應(yīng)“standard”和“singleInstance”啟動模式的標(biāo)識棋凳。是的,android api文檔中確實只只有以上三種標(biāo)識用來設(shè)置啟動模式剩岳,而且第三種“**FLAG_ACTIVITY_CLEAR_TOP
**”還不對應(yīng)任何一種啟動模式贞滨,難道可以算作第5種啟動模式?遺憾的是疲迂,我現(xiàn)在也不清楚是怎么回事。
  
但是實際開發(fā)中莫湘,我們一般都是在AndroidMenifest.xml文件中去設(shè)置Activity的啟動模式。
  
好了幅垮,文章差不多就寫到這吧。謝謝觀看~

多說一句尾组,關(guān)于Android任務(wù)和返回棧的相關(guān)知識可以參見Android api文檔讳侨,上面說的很詳細潮峦,其實我習(xí)慣把返回棧說成是任務(wù)棧囱皿,不知道這樣是不是合適,嘿嘿~

參考文獻:
https://developer.android.com/guide/topics/manifest/activity-element.html
https://developer.android.com/guide/components/tasks-and-back-stack.html
https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拘悦,一起剝皮案震驚了整個濱河市齿兔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌础米,老刑警劉巖分苇,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異屁桑,居然都是意外死亡组砚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門掏颊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糟红,“玉大人,你說我怎么就攤上這事乌叶∨璩ィ” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵准浴,是天一觀的道長事扭。 經(jīng)常有香客問我,道長乐横,這世上最難降的妖魔是什么求橄? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮葡公,結(jié)果婚禮上罐农,老公的妹妹穿的比我還像新娘。我一直安慰自己催什,他們只是感情好涵亏,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蒲凶,像睡著了一般气筋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旋圆,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天宠默,我揣著相機與錄音,去河邊找鬼灵巧。 笑死搀矫,一個胖子當(dāng)著我的面吹牛抹沪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艾君,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼采够,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了冰垄?” 一聲冷哼從身側(cè)響起蹬癌,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虹茶,沒想到半個月后逝薪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡蝴罪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年董济,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片要门。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡虏肾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出欢搜,到底是詐尸還是另有隱情封豪,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布炒瘟,位于F島的核電站吹埠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏疮装。R本人自食惡果不足惜缘琅,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望廓推。 院中可真熱鬧刷袍,春花似錦、人聲如沸受啥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滚局。三九已至,卻和暖如春顽频,著一層夾襖步出監(jiān)牢的瞬間藤肢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工糯景, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘁圈,地道東北人省骂。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像最住,于是被迫代替她去往敵國和親钞澳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

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