1.2 Activity的啟動模式和Flags

Android開發(fā)
Activity的啟動模式
1. Activity的LaunchMode

我們知道互例,在默認情況下还栓,當我們多次啟動同一個Activity的時候锦庸,系統(tǒng)會創(chuàng)建多個實例并把它們一一放入任務(wù)棧中块蚌,當我們單擊back鍵,會發(fā)現(xiàn)這些Activity會一一回退膘格。任務(wù)棧是一種后進先出的棧結(jié)構(gòu)峭范,每按一下back鍵就會有一個Activity出棧,直到棻窦空為止纱控,當棧中無任何Activity的時候,系統(tǒng)就會回收這個任務(wù)棧菜秦。

目前有四種啟動模式:standard甜害,singleTopsingleTasksingleInstance球昨。

(1). standard:標準模式
這是系統(tǒng)的默認模式尔店,每次啟動一個Activity都會重新創(chuàng)建一個新的實例,不管這個實例是否已經(jīng)存在主慰。被創(chuàng)建的實例的生命周期符合典型情況下Activity的生命周期嚣州,如上節(jié)所述,它的onCreate共螺,onStart该肴,onResume都會被調(diào)用。這是一種典型的多實例實現(xiàn)藐不,一個任務(wù)棧中可以有多個實例匀哄,每個實例也可以屬于不同的任務(wù)棧。在這種模式下雏蛮,誰啟動了這個Activity涎嚼,那么這個Activity就運行在啟動它的那個Activity所在的棧中。比如ActivityA啟動了ActivityB(B是標準模式)挑秉,那么B就會進入到A所在的棧中铸抑。不知你們發(fā)現(xiàn)沒有,當我們用ApplicationContext去啟動standard模式的Activity的時候衷模,會出現(xiàn)錯誤鹊汛。

這是因為standard模式的Activity默認會進入啟動它的Activity所屬的任務(wù)棧中,但是由于非Activity類型的Context(如ApplicationContext)并沒有所謂的任務(wù)棧阱冶,所以這就有問題了刁憋。解決這個問題的方法是為待啟動Activity指定FLAG_ACTIVITY_NEW_TASK標記位,這樣啟動的時候就會為它創(chuàng)建一個新的任務(wù)棧木蹬,這個時候待啟動Activity實際上是以singleTask模式啟動的至耻。

(2). singleTop:棧頂復(fù)用模式
在這種模式下,如果新Activity已經(jīng)位于任務(wù)棧的棧頂,那么此Activity不會被重新創(chuàng)建尘颓,同時它的onNewIntent方法會被回調(diào)走触,通過此方法的參數(shù)我們可以取出當前請求的信息,需要注意的是疤苹,這個ActivityonCreate互广,onStart不會被系統(tǒng)調(diào)用,因為它并沒有發(fā)生改變卧土。如果新Activity的實例已存在但不是位于棧頂惫皱,那么新Activity仍然會重新重建。

(3). singleTask:棧內(nèi)復(fù)用模式
這是一種單實例模式尤莺,在這種模式下旅敷,只要Activity在一個棧中存在,那么多次啟動此Activity都不會重新創(chuàng)建實例颤霎,和singleTop一樣媳谁,系統(tǒng)也會回調(diào)其onNewIntent。具體一點友酱,當一個具有singleTask模式的Activity請求啟動后韩脑,比如ActivityA,系統(tǒng)首先會尋找是否存在A想要的任務(wù)棧粹污,如果不存在段多,就重新創(chuàng)建一個任務(wù)棧,然后創(chuàng)建A的實例后把A放到棧中壮吩。如果存在A所需的任務(wù)棧进苍,這時要看A是否在棧中有實例存在,如果有實例存在鸭叙,那么系統(tǒng)就會把A調(diào)到棧頂并調(diào)用它的onNewIntent方法觉啊,如果實例不存在,就創(chuàng)建A的實例并把A壓入棧中沈贝。

舉幾個例子:

  • 比如目前任務(wù)棧S1中的情況為ABC杠人,這個時候Activity DsingleTask模式請求啟動,其所需要的任務(wù)棧為S2宋下,由于S2D的實例均不存在嗡善,所以系統(tǒng)會先創(chuàng)建任務(wù)棧S2,然后再創(chuàng)建D的實例并將其入棧到S2.
  • 另外一種情況学歧,假設(shè)D所需的任務(wù)棧為S1罩引,其他情況如上面例子,那么S1已經(jīng)存在枝笨,所以系統(tǒng)會直接創(chuàng)建D的實例并將其入棧到S1袁铐。
  • 如果D所需的任務(wù)棧為S1揭蜒,并且當前任務(wù)棧S1的情況為ADBC,根據(jù)棧內(nèi)復(fù)用的原則剔桨,此時D不會重新創(chuàng)建屉更,系統(tǒng)會把D切換到棧頂并調(diào)用其onNewIntent方法,同時由于singleTask默認具有clearTop的效果洒缀,會導(dǎo)致棧內(nèi)所有在D上面的Activity全部出棧瑰谜,于是最終S1中的情況為AD,這一點比較特殊帝洪,在后面還會對此種情況詳細地分析。

(4) singleInstance:單實例模式
這是一種加強的singleTask模式脚猾,它除了具有singleTask模式的所有特性外葱峡,還加強了一點,那就是具有此種模式的Activity只能單獨地位于一個任務(wù)棧中龙助,換句話說砰奕,比如Activity AsingleInstance模式,當 A 啟動后提鸟,系統(tǒng)會為它創(chuàng)建一個新的任務(wù)棧军援,然后A獨自在這個新的任務(wù)棧中,由于棧內(nèi)復(fù)用的特性称勋,后續(xù)的請求均不會創(chuàng)建新的Activity胸哥,除非這個獨特的任務(wù)棧被系統(tǒng)銷毀了。

這里需要指出一種情況赡鲜,我們假設(shè)目前有2個任務(wù)棧空厌,前臺任務(wù)棧的情況為AB,而后臺任務(wù)棧的情況為CD银酬,這里假設(shè)CD的啟動模式均為singleTask〕案現(xiàn)在請求啟動D,那么整個后臺任務(wù)棧都會被切換到前臺揩瞪,這個時候整個后退列表變成了ABCD。當用戶按Back鍵的時候,列表中的Activity會一一出棧寒锚。
如下圖:

image

如果不是請求啟動D而是啟動C垫蛆,那么情況就不一樣了。
如下圖:
image

另外一問題是嗤攻,在singleTask啟動模式中琳拨,多次提到某個Activity所需的任務(wù)棧,什么是Activity所需要的任務(wù)棧呢屯曹?這要從一個參數(shù)說起:TaskAffinity狱庇,可以翻譯為任務(wù)相關(guān)性惊畏。這個參數(shù)標識了一個Activity所需要的任務(wù)棧的名字,默認情況下密任,所有Activity所需的任務(wù)棧的名字為應(yīng)用的包名颜启。當然,我們可以為每個Activity都單獨指定TaskAffinity屬性浪讳,這個屬性必須不能和包名相同缰盏,否則就相當于沒有指定。TaskAffinity屬性主要和singleTask啟動模式或者allowTaskReparenting屬性配對使用淹遵,在其他情況下沒有意義口猜。另外,任務(wù)棧分為前臺任務(wù)棧和后臺任務(wù)棧透揣,后臺任務(wù)棧中的Activity位于暫停狀態(tài)济炎,用戶可以通過切換將后臺任務(wù)棧再次調(diào)到前臺。

TaskAffinitysingleTask啟動模式配對使用的時候辐真,它是具有該模式的Activity的目前任務(wù)棧的名字须尚,待啟動的Activity會運行在名字和TaskAffinity相同的任務(wù)棧中。

TaskAffinityallowTaskReparenting結(jié)合的時候侍咱,這種情況比較復(fù)雜耐床,會產(chǎn)生特殊的效果。當一個應(yīng)用A啟動了應(yīng)用B的某個Activity后楔脯,如果這個ActivityallowTaskReparenting屬性為true的話撩轰,那么當應(yīng)用B被啟動后,此Activity會直接從應(yīng)用A的任務(wù)棧轉(zhuǎn)移到應(yīng)用B的任務(wù)棧中昧廷。這還是很抽象钧敞,在具體點,比如現(xiàn)在有兩個應(yīng)用AB麸粮,A啟動了B的一個ActivityC溉苛,然后按Home鍵回到桌面,然后再單擊B的桌面圖標弄诲,這個時候并不是啟動了B的主Activity愚战,而是重新顯示了已經(jīng)被應(yīng)用A啟動的ActivityC,或者說齐遵,CA的任務(wù)棧轉(zhuǎn)移到了B的任務(wù)棧中寂玲。可以這么理解梗摇,由于A啟動了C拓哟,這個時候C只能運行在A的任務(wù)棧中,但是C屬于B應(yīng)用伶授,正常情況下断序,它的TaskAffinity肯定不可能和A的任務(wù)棧相同(因為包名不同)流纹,所以,當B被啟動后违诗,B會創(chuàng)建自己的任務(wù)棧漱凝,這個時候系統(tǒng)發(fā)現(xiàn)C原本想要的任務(wù)棧已經(jīng)被創(chuàng)建了,所以就把CA的任務(wù)棧中轉(zhuǎn)移過來了诸迟。

如何給Activity指定啟動模式呢茸炒?有兩種方法,第一種是通過AndroidMenifestActivity指定啟動模式阵苇。

<activity 
    android:name=".MainActivity"
    android:launchMode="singleTask">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

另一種情況是通過在Intent中設(shè)置標志位來為Activity指定啟動模式:

Intent intent = new Intent();
intent.setClass(MainActivity.class,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

這兩種方式都可以為Activity指定啟動模式壁公,但是兩者還是有區(qū)別的。首先绅项,優(yōu)先級上紊册,第二種方式的優(yōu)先級要高于第一種,當兩種同時存在時趁怔,以第二種方式為準湿硝;其次薪前,上述兩種方式在限定范圍上有所不同润努,比如,第一種方式無法直接為Activity設(shè)定FLAG_ACTIVITY_CLEAR_TASK標識示括,而第二種方式無法為Activity指定singleInstance模式铺浇。
standardsingleTop都比較好理解,singleInstance由于其特殊性也好理解垛膝,但是關(guān)于singleTask有一種情況需要再說明一下鳍侣。如上圖所示,如果在ActivityB中請求的不是D而是C吼拥,那么情況如何呢倚聚?答案是任務(wù)棧列表變成了ABCActivityD被直接出棧了凿可。

<activity 
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize"
    android:launchMode="standard">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity 
    android:name=".SecondActivity" 
    android:taskAffinity="com.ryg.task1"
    android:launchMode="singleTask"/>

<activity 
    android:name=".ThirdActivity"
    android:taskAffinity="com.ryg.task1"
    android:launchMode="singleTask">
    
</activity>

我們將SecondActivityThirdActivity都設(shè)成singleTask并指定它們的taskAffinity屬性為"com.ryg.task1"惑折,注意這個taskAffinity屬性的值為字符串,且中間必須含有包名分隔符"."枯跑。然后做如下操作惨驶,在MainActivity中單擊按鈕啟動SecondActivity,在SecondActivity中單擊按鈕啟動ThirdActivity敛助,在ThirdActivity中單擊按鈕又啟動MainActivity粗卜,最后再在MainActivity中單擊按鈕啟動SecondActivity,現(xiàn)在按2次back鍵纳击,然后看到的是哪個Activity续扔?答案是回到桌面攻臀。

首先,從理論上分析這個問題测砂,先假設(shè)MainActivityA茵烈,SecondActivityBThirdActivityC砌些。我們知道Astandard模式呜投,按照規(guī)定,AtaskAffinity值繼承自ApplicationtaskAffinity存璃,而Application的默認taskAffinity為包名仑荐,所以AtaskAffinity為包名。由于我們在XML中為BC指定了taskAffinity和啟動模式纵东,所以BCsingleTask模式且有相同的taskAffinity“com.ryg.task1”粘招。A啟動B的時候,按照singleTask的規(guī)則偎球,這個時候需要為B重新創(chuàng)建一個任務(wù)棧"com.ryg.task1"洒扎。B再啟動C,按照singleTask的規(guī)則衰絮,由于C所需的任務(wù)棧(和B為同一任務(wù)棧)已經(jīng)被B創(chuàng)建袍冷,所以無需在創(chuàng)建新的任務(wù)棧,這個時候系統(tǒng)只是創(chuàng)建C的實例后將C入棧了猫牡。接著C在啟動A胡诗,Astandard模式,所以系統(tǒng)會為它創(chuàng)建一個新的實例并將它加到啟動它的那個Activity的任務(wù)棧淌友,由于是C啟動了A煌恢,所以A會進入C的任務(wù)棧中并位于棧頂。這個時候已經(jīng)有兩個任務(wù)棧了震庭,一個是名字為包名的任務(wù)棧瑰抵,里面只有A,另一個是名字為"com.ryg.task1"的任務(wù)棧器联,里面的ActivityBCA二汛。接下來,A再啟動B主籍,由于BsingleTask习贫,B需要回到任務(wù)棧的棧頂,由于棧的工作模式為“后進新出”千元,B想要回到棧頂苫昌,只能是CA出棧。所以幸海,到這里就很好理解了祟身,如果再按back鍵奥务,B就出棧了,B所在的任務(wù)棧已經(jīng)不存在了袜硫,這個時候只能是回到后臺任務(wù)棧氯葬,并把A顯示出來。注意這個A是后臺任務(wù)棧的A婉陷,不是"com.ryg.task1"任務(wù)棧的A帚称,接著在繼續(xù)back,就回到桌面了秽澳。分析到這里闯睹,我們得出一條結(jié)論,"singleTask"模式的Activity切換到棧頂會導(dǎo)致在它之上的棧內(nèi)的Activity出棧担神。

Activity的Flags

ActivityFlag有很多楼吃,這里主要分析一些比較常用的標記位。標記位的作用很多妄讯,有的標記位可以設(shè)定Activity的啟動模式孩锡,比如FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_SINGLE_TOP等,還有的標記位可以影響Activity的運行狀態(tài)亥贸,比如FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_EXCLUDE_FROM_RECENTS等躬窜。

大部分情況下,我們不需要為Activity指定標記位砌函,因此斩披,對于標記位理解即可溜族。在使用標記位的時候讹俊,要注意有些標記位是系統(tǒng)內(nèi)部使用的,應(yīng)用程序不需要去手動設(shè)置這些標記位以防止出現(xiàn)問題煌抒。

FLAG_ACTIVITY_NEW_TASK
這個標記位的作用是為Activity指定singleTask啟動模式仍劈,其效果和在XML中指定該啟動模式相同。

FLAG_ACTIVITY_SINGLE_TOP
這個標記位的作用是為Activity指定singleTop啟動模式寡壮,其效果和在XML中指定該啟動模式相同贩疙。

FLAG_ACTIVITY_CLEAR_TOP
具有此標記位的Activity,當它啟動時况既,在同一個任務(wù)棧中所有位于它上面的Activity都要出棧这溅。這個標記位一般會和singleTask啟動模式一起出現(xiàn),在這種情況下棒仍,被啟動Activity的實例如果已經(jīng)存在悲靴,那么系統(tǒng)就會調(diào)用它的onNewIntent。如果被啟動的Activity采用standard模式啟動莫其,那么它連同它之上的Activity都要出棧癞尚,系統(tǒng)會創(chuàng)建新的Activity實例并放入棧頂耸三。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有這個標記位的Activity不會出現(xiàn)在歷史Activity的列表中,當某些情況下我們不希望用戶通過歷史列表回到我們的Activity的時候這個標記比較有用浇揩,它等同于在XML中指定Activity的屬性android:excludeFromRecents="true"仪壮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市胳徽,隨后出現(xiàn)的幾起案子积锅,更是在濱河造成了極大的恐慌,老刑警劉巖养盗,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乏沸,死亡現(xiàn)場離奇詭異,居然都是意外死亡爪瓜,警方通過查閱死者的電腦和手機蹬跃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铆铆,“玉大人蝶缀,你說我怎么就攤上這事”』酰” “怎么了翁都?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谅猾。 經(jīng)常有香客問我柄慰,道長,這世上最難降的妖魔是什么税娜? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任坐搔,我火速辦了婚禮,結(jié)果婚禮上敬矩,老公的妹妹穿的比我還像新娘概行。我一直安慰自己,他們只是感情好弧岳,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布凳忙。 她就那樣靜靜地躺著,像睡著了一般禽炬。 火紅的嫁衣襯著肌膚如雪涧卵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天腹尖,我揣著相機與錄音柳恐,去河邊找鬼。 笑死,一個胖子當著我的面吹牛胎撤,可吹牛的內(nèi)容都是我干的晓殊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伤提,長吁一口氣:“原來是場噩夢啊……” “哼巫俺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肿男,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤介汹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舶沛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘹承,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年如庭,在試婚紗的時候發(fā)現(xiàn)自己被綠了叹卷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡坪它,死狀恐怖骤竹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情往毡,我是刑警寧澤蒙揣,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站开瞭,受9級特大地震影響懒震,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嗤详,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一个扰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧断楷,春花似錦锨匆、人聲如沸崭别。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茅主。三九已至舞痰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诀姚,已是汗流浹背响牛。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呀打。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓矢赁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贬丛。 傳聞我的和親對象是個殘疾皇子撩银,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354

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