探索Activity之launchMode

之前有簡(jiǎn)單探索了Activity的生命周期, 也提到, Activity的生命周期實(shí)際場(chǎng)景遠(yuǎn)非那么簡(jiǎn)單, 諸如launch mode, intent flag, activity屬性等都會(huì)對(duì)生命周期流程產(chǎn)生影響.

本想將launchMode, intent flag, activity任務(wù)相關(guān)屬性(affinity, clearTaskOnLaunch, finishOnTaskLaunch等)一起結(jié)合task和back stack探究以下, 發(fā)現(xiàn)關(guān)于intent flag的使用和官方文檔有很多出入, 在此先探索下啟動(dòng)模式launchMode.

google發(fā)現(xiàn), 國(guó)外很多人對(duì)Android官方文檔的task and back stack這塊有質(zhì)疑, 也有Android的開(kāi)發(fā)人員出來(lái)解釋是由于代碼更新, 文檔沒(méi)有跟上~~
看來(lái)果然是盡信書(shū)不如無(wú)書(shū)啊, 下次結(jié)合實(shí)例深入研究下這塊.

1, 相關(guān)概念

根據(jù)官方解釋:

The launchMode attribute specifies an instruction on how the activity should be launched into a task.

大意是說(shuō)launchMode指示一個(gè)activity是以何種形式被啟動(dòng)(放置)到一個(gè)task中的.
還是涉及到task了, 先了解下吧:

  • Task
    • 任務(wù)
    • 是指在執(zhí)行特定作業(yè)時(shí)與用戶交互的一系列 Activity.
  • Back Stack
    • 返回棧
    • 組成任務(wù)的這些Activity按照各自的打開(kāi)順序排列在堆棧中.

以上是官方文檔的解釋.
這里有一個(gè)疑問(wèn), Task和Back Stack的對(duì)應(yīng)關(guān)系是怎樣的呢, 一一對(duì)應(yīng), 還是說(shuō)系統(tǒng)就一個(gè)back stack, 所有的activity都在stack中呢? 這個(gè)疑問(wèn)我們?cè)谔剿鱨aunchMode的過(guò)程中也會(huì)有解答~

  • launchMode

    • 有四種(standard, singleTop, singleTask, singleInstance), 默認(rèn)standard.
  • standard

    • Activity 可以多次實(shí)例化声登,而每個(gè)實(shí)例均可屬于不同的任務(wù)驱入,并且一個(gè)任務(wù)可以擁有多個(gè)實(shí)例拾枣。
  • singleTop

    • 如果當(dāng)前任務(wù)的頂部已存在 Activity 的一個(gè)實(shí)例,則系統(tǒng)會(huì)通過(guò)調(diào)用該實(shí)例的 onNewIntent() 方法向其傳送 Intent,而不是創(chuàng)建 Activity 的新實(shí)例.
    • Activity 可以多次實(shí)例化铝噩,而每個(gè)實(shí)例均可屬于不同的任務(wù)趟庄,并且一個(gè)任務(wù)可以擁有多個(gè)實(shí)例.
  • singleTask

    • 系統(tǒng)創(chuàng)建新任務(wù)并實(shí)例化位于新任務(wù)底部的 Activity. 但是显拳,如果該 Activity 的一個(gè)實(shí)例已存在于一個(gè)單獨(dú)的任務(wù)中,則系統(tǒng)會(huì)通過(guò)調(diào)用現(xiàn)有實(shí)例的 onNewIntent() 方法向其傳送 Intent搓萧,而不是創(chuàng)建新實(shí)例.
    • 一次只能存在 Activity 的一個(gè)實(shí)例.
  • singleInstance

    • 與 "singleTask" 相同杂数,只是系統(tǒng)不會(huì)將任何其他 Activity 啟動(dòng)到包含實(shí)例的任務(wù)中.
    • 該 Activity 始終是其任務(wù)唯一僅有的成員;由此 Activity 啟動(dòng)的任何 Activity 均在單獨(dú)的任務(wù)中打開(kāi).

以上概念解釋都來(lái)自官方文檔.

我們接下來(lái)要做的就是編寫(xiě)實(shí)例加以驗(yàn)證, 并對(duì)我們之前探索的Activity的生命周期加以補(bǔ)充.

2, 開(kāi)始探索

借用上次探索生命周期的Demo程序.
Github源碼地址

我們有三個(gè)activity: AActivity, BActivity, CActivity.
下面我們圍繞這三個(gè)activity開(kāi)展一系列實(shí)驗(yàn).

2.1, Standard模式

2.1.1, 都為standard, 執(zhí)行A -> B -> C -> C

生命周期Log:


Task/Back Stack信息:

  Stack #1 mStackId=55:
    Task id #193
    * TaskRecord{4325d0a0 #193 A=com.anly.samples U=0 sz=5}
      numActivities=5 rootWasReset=true userId=0 mTaskType=0 numFullscreen=5 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[
      ActivityRecord{432654c8 u0 com.anly.samples/.MainActivity t193},
      ActivityRecord{4366fbd0 u0 com.anly.samples/.activity.AActivity t193}, 
      ActivityRecord{429e0cc0 u0 com.anly.samples/.activity.BActivity t193}, 
      ActivityRecord{43590f20 u0 com.anly.samples/.activity.CActivity t193}, 
      ActivityRecord{427f6d98 u0 com.anly.samples/.activity.CActivity t193}]

可以看到:
1, C啟動(dòng)C時(shí), 新建了一個(gè)C的實(shí)例.
2, 此時(shí)Task #193 中的實(shí)例如同我們的啟動(dòng)順序依次是ABCC.

2.2 SingleTop模式

2.2.1, C設(shè)置為singleTop, 執(zhí)行A -> B -> C -> C

生命周期Log:


Task/Back Stack信息:

Stack #1 mStackId=24:
    Task id #155
    * TaskRecord{432ae270 #155 A=com.anly.samples U=0 sz=5}
      numActivities=5 rootWasReset=false userId=0 mTaskType=0 numFullscreen=5 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427bf238 u0 com.anly.samples/.MainActivity t155}, 
      ActivityRecord{427c1c28 u0 com.anly.samples/.activity.AActivity t155}, 
      ActivityRecord{427c2b80 u0 com.anly.samples/.activity.BActivity t155}, 
      ActivityRecord{427c34c8 u0 com.anly.samples/.activity.CActivity t155}]

可以看到:
1, C啟動(dòng)C時(shí), 并未重建一個(gè)C, 還是使用了之前的實(shí)例, 通過(guò)onNewIntent的方式喚起.
2, 需要注意的時(shí), 就算是C復(fù)用了, 還是會(huì)執(zhí)行C的onPause, 然后再onNewIntent --> onResume的.
3, 此時(shí)Task #155的Activity實(shí)例順序是ABC.

2.2.2, B設(shè)置為singleTop, 執(zhí)行A -> B -> C -> B

生命周期Log:


Task/Back Stack信息:

  Stack #1 mStackId=57:
    Task id #195
    * TaskRecord{42fe9718 #195 A=com.anly.samples U=0 sz=5}
      numActivities=5 rootWasReset=false userId=0 mTaskType=0 numFullscreen=5 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427f48c8 u0 com.anly.samples/.MainActivity t195}, 
      ActivityRecord{43355cd8 u0 com.anly.samples/.activity.AActivity t195}, 
      ActivityRecord{429984d8 u0 com.anly.samples/.activity.BActivity t195}, 
      ActivityRecord{4484a590 u0 com.anly.samples/.activity.CActivity t195}, 
      ActivityRecord{43953fe8 u0 com.anly.samples/.activity.BActivity t195}]

可以看到:
1, 從C啟動(dòng)B時(shí), 又新建了一個(gè)B的實(shí)例.
2, 此時(shí)Task #195的Activity實(shí)例順序?yàn)锳BCB, 看起來(lái), 這個(gè)和2.1.1的standard模式實(shí)驗(yàn)一樣, 都是新建了實(shí)例.

2.3 SingleTask模式

2.3.1 設(shè)置C為singleTask, 執(zhí)行A -> B -> C -> C

生命周期Log:


Task/Back Stack信息:

Stack #1 mStackId=26:
    Task id #157
    * TaskRecord{43025e70 #157 A=com.anly.samples U=0 sz=4}
      numActivities=4 rootWasReset=false userId=0 mTaskType=0 numFullscreen=4 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427bbd48 u0 com.anly.samples/.MainActivity t157}, 
      ActivityRecord{43bc91c8 u0 com.anly.samples/.activity.AActivity t157}, 
      ActivityRecord{42a239b8 u0 com.anly.samples/.activity.BActivity t157}, 
      ActivityRecord{430e21c0 u0 com.anly.samples/.activity.CActivity t157}]

可以看到結(jié)果和2.2.1一樣
1, C復(fù)用了. Task里的Activity依次為ABC.
2, C先onPause, 然后onNewIntent喚起, 走onResume.

2.3.2, 設(shè)置B為singleTask, 執(zhí)行A -> B -> C -> B

生命周期Log:


Task/Back Stack信息:

Stack #1 mStackId=27:
    Task id #158
    * TaskRecord{42ccb098 #158 A=com.anly.samples U=0 sz=3}
      numActivities=3 rootWasReset=false userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427bbd48 u0 com.anly.samples/.MainActivity t158}, 
      ActivityRecord{43082eb0 u0 com.anly.samples/.activity.AActivity t158}, 
      ActivityRecord{43aaea90 u0 com.anly.samples/.activity.BActivity t158}]

可以看到:
1, B是復(fù)用的, onNewIntent喚起, 走的onRestart流程.
2, Task #158的Activity實(shí)例依次是AB.
3, C沒(méi)有主動(dòng)銷毀(Back, finish), 但是被移除了.

2.4, SingleInstance模式

2.4.1, 設(shè)置C為singleInstance, 執(zhí)行A -> B -> C -> C

生命周期Log:


Task/Back Stack信息:

Stack #1 mStackId=31:
    Task id #163
    * TaskRecord{43308db8 #163 A=com.anly.samples U=0 sz=1}
      numActivities=1 rootWasReset=false userId=0 mTaskType=0 numFullscreen=1 mOnTopOfHome=false
      affinity=com.anly.samples
      intent={cmp=com.anly.samples/.activity.CActivity}
      realActivity=com.anly.samples/.activity.CActivity
      Activities=[ActivityRecord{432f03c0 u0 com.anly.samples/.activity.CActivity t163}]

    Task id #162
    * TaskRecord{42d702b0 #162 A=com.anly.samples U=0 sz=3}
      numActivities=3 rootWasReset=false userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427bc7e8 u0 com.anly.samples/.MainActivity t162}, 
      ActivityRecord{42995d98 u0 com.anly.samples/.activity.AActivity t162}, 
      ActivityRecord{4326ee40 u0 com.anly.samples/.activity.BActivity t162

可以看到:
1, 第一次B啟動(dòng)C時(shí), C運(yùn)行在了另一個(gè)Task #163中.
2, 從C再次啟動(dòng)C室, C復(fù)用了, 類似的, onNewIntent喚起, 走onResume流程.
3, 特別注意: task信息中, 可以看到Stack和Task的關(guān)系. 此時(shí), Stack #1中有兩個(gè)Task, 分別是Task #162(AB)在Stack底部, Task #163(C)在Stack頂部.

2.4.2, 設(shè)置A為singleInstance, 執(zhí)行A -> B -> C -> A

生命周期Log:


Task/Back Stack信息:

Stack #1 mStackId=34:
    Task id #169
    * TaskRecord{431fd9e0 #169 A=com.anly.samples U=0 sz=1}
      numActivities=1 rootWasReset=false userId=0 mTaskType=0 numFullscreen=1 mOnTopOfHome=false
      affinity=com.anly.samples
      intent={flg=0x400000 cmp=com.anly.samples/.activity.AActivity}
      realActivity=com.anly.samples/.activity.AActivity
      Activities=[ActivityRecord{43a58140 u0 com.anly.samples/.activity.AActivity t169}]

    Task id #168
    * TaskRecord{437328f0 #168 A=com.anly.samples U=0 sz=3}
      numActivities=3 rootWasReset=true userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=false
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{43114528 u0 com.anly.samples/.MainActivity t168}, 
      ActivityRecord{42ff3f20 u0 com.anly.samples/.activity.BActivity t168}, 
      ActivityRecord{42abe0a0 u0 com.anly.samples/.activity.CActivity t168}]

可以看到結(jié)果和2.4.1類似
1, A單啟在一個(gè)Task #169中, 再次啟動(dòng)不重建, onNewIntent喚起, 走onRestart流程.
2, 需要注意的是, 從A啟動(dòng)B的時(shí)候, B是跟MainActiviy在一個(gè)task的, 也就是說(shuō)A所在的task容不下別人.
3, 需要強(qiáng)調(diào)的是, A是復(fù)用的, 這個(gè)時(shí)候如果一直按Back鍵返回, 不會(huì)再看到A了, 如下:

結(jié)論

根據(jù)以上實(shí)驗(yàn), 可以得出以下結(jié)論:

  1. standard模式的Activity, 每次啟動(dòng)都會(huì)創(chuàng)建一個(gè)新的實(shí)例, 放到啟動(dòng)他的那個(gè)Activity所在的Task中.

  2. singleTop模式的Activity, 僅當(dāng)該Activity已經(jīng)在Task的頂部了, 才會(huì)復(fù)用. 復(fù)用時(shí)onPause, 然后onNewIntent喚起, 走onResume流程. 否則都要?jiǎng)?chuàng)建新的實(shí)例, 放進(jìn)Task中.

  3. singleTask模式的Activity, 同一個(gè)Task中只會(huì)存在一個(gè)實(shí)例. 如果Task中還沒(méi)有, 則新建, 放在Task頂部; 如果Task中已經(jīng)有該Activity實(shí)例, 則復(fù)用.

  4. singleTask模式的Activity的復(fù)用模式:

    • 如果已經(jīng)在Task頂部, 如同singleTop的復(fù)用模式;
    • 如果不在Task頂部, 則銷毀Task中該Activity頂部的所有其他Activity, 通過(guò)onNewIntent喚起該Activity, 走onRestart流程.
  5. singleInstance模式的Activity, 會(huì)運(yùn)行在一個(gè)單獨(dú)的Task中, 且整個(gè)系統(tǒng)中只有一個(gè)該Activity實(shí)例. 相當(dāng)于單例模式. 復(fù)用模式和singleTask一樣.

  6. 回答文首提出的Task和Back Stack的關(guān)系:

    • 系統(tǒng)中會(huì)存在多個(gè)Task, 多個(gè)Back Stack.
    • 其中一個(gè)Back Stack中可以有多個(gè)Task.
    • Task可以理解為一次交互的Activities的組合(一般來(lái)說(shuō)一個(gè)Application的所有Activity運(yùn)行在一個(gè)Task).
    • Back Stack可以理解為從Launcher界面進(jìn)入某一個(gè)應(yīng)用開(kāi)始交互, 可能有很多操作, 這些操作可能分成不同任務(wù)的, 例如在編輯聯(lián)系人的時(shí)候跳轉(zhuǎn)到相冊(cè)了, 可能新啟了一個(gè)Task, 但是這整個(gè)交互流程都在一個(gè)Back Stack.
    • 簡(jiǎn)單來(lái)說(shuō), 個(gè)人理解, Back Stack的重點(diǎn)是在Back, 就是說(shuō)同一Stack的Activity是可以一直返回的.
  7. 多次實(shí)驗(yàn), 大家也看到了onPause的重要性, 各種流程, onPause都是必不可少的. 這也給了我們App處理的很多啟示. 例如重要數(shù)據(jù)的保存, 另外, 如生命周期一文中所說(shuō), 也反映了別的Activity啟動(dòng)是需要等到上一個(gè)Activity onPause執(zhí)行完畢的.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘸洛,一起剝皮案震驚了整個(gè)濱河市揍移,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌反肋,老刑警劉巖那伐,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異石蔗,居然都是意外死亡罕邀,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)养距,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诉探,“玉大人,你說(shuō)我怎么就攤上這事棍厌∩隹瑁” “怎么了竖席?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)敬肚。 經(jīng)常有香客問(wèn)我毕荐,道長(zhǎng),這世上最難降的妖魔是什么艳馒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任憎亚,我火速辦了婚禮,結(jié)果婚禮上鹰溜,老公的妹妹穿的比我還像新娘虽填。我一直安慰自己,他們只是感情好曹动,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布斋日。 她就那樣靜靜地躺著,像睡著了一般墓陈。 火紅的嫁衣襯著肌膚如雪恶守。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天贡必,我揣著相機(jī)與錄音兔港,去河邊找鬼。 笑死仔拟,一個(gè)胖子當(dāng)著我的面吹牛衫樊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播利花,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼科侈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了炒事?” 一聲冷哼從身側(cè)響起臀栈,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挠乳,沒(méi)想到半個(gè)月后权薯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡睡扬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年盟蚣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卖怜。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡刁俭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出韧涨,到底是詐尸還是另有隱情牍戚,我是刑警寧澤侮繁,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站如孝,受9級(jí)特大地震影響宪哩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜第晰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一锁孟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茁瘦,春花似錦品抽、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至腔稀,卻和暖如春盆昙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背焊虏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工淡喜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诵闭。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓炼团,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親疏尿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瘟芝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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