之前有簡(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é)論:
standard模式的Activity, 每次啟動(dòng)都會(huì)創(chuàng)建一個(gè)新的實(shí)例, 放到啟動(dòng)他的那個(gè)Activity所在的Task中.
singleTop模式的Activity, 僅當(dāng)該Activity已經(jīng)在Task的頂部了, 才會(huì)復(fù)用. 復(fù)用時(shí)onPause, 然后onNewIntent喚起, 走onResume流程. 否則都要?jiǎng)?chuàng)建新的實(shí)例, 放進(jìn)Task中.
singleTask模式的Activity, 同一個(gè)Task中只會(huì)存在一個(gè)實(shí)例. 如果Task中還沒(méi)有, 則新建, 放在Task頂部; 如果Task中已經(jīng)有該Activity實(shí)例, 則復(fù)用.
-
singleTask模式的Activity的復(fù)用模式:
- 如果已經(jīng)在Task頂部, 如同singleTop的復(fù)用模式;
- 如果不在Task頂部, 則銷毀Task中該Activity頂部的所有其他Activity, 通過(guò)onNewIntent喚起該Activity, 走onRestart流程.
singleInstance模式的Activity, 會(huì)運(yùn)行在一個(gè)單獨(dú)的Task中, 且整個(gè)系統(tǒng)中只有一個(gè)該Activity實(shí)例. 相當(dāng)于單例模式. 復(fù)用模式和singleTask一樣.
-
回答文首提出的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是可以一直返回的.
多次實(shí)驗(yàn), 大家也看到了onPause的重要性, 各種流程, onPause都是必不可少的. 這也給了我們App處理的很多啟示. 例如重要數(shù)據(jù)的保存, 另外, 如生命周期一文中所說(shuō), 也反映了別的Activity啟動(dòng)是需要等到上一個(gè)Activity onPause執(zhí)行完畢的.