四種啟動(dòng)模式
Standard
標(biāo)準(zhǔn)模式,每當(dāng)有一次Intent請求员凝,就會(huì)創(chuàng)建一個(gè)新的Activity實(shí)例。
-
Android 5.0 之前
- 同一應(yīng)用內(nèi)
新生成的Activity会放,放入發(fā)送Intent者Task的棧頂监氢。
```
TaskRecord{537925a8 #42 A com.zlq.lmt U 0}
Run #3: ActivityRecord{538314d0 com.zlq.lmt/.StandardActivity}
Run #2: ActivityRecord{5385a7c4 com.zlq.lmt/.StandardActivity}
Run #1: ActivityRecord{53760908 com.zlq.lmt/.MainActivity}
2. 跨應(yīng)用啟動(dòng)
新生成的Activity,放入發(fā)送Intent者Task的棧的棧頂(盡管他們屬于不同的程序管闷,還是會(huì)放入調(diào)用者程序的棧內(nèi))。
```
TaskRecord{537df318 #52 A com.zlq.bbb U 0}
Run #2: ActivityRecord{537a889c com.zlq.lmt/.StandardActivity}
Run #1: ActivityRecord{537a4a5c com.zlq.bbb/.MainActivityB}
這時(shí)窃肠,我們打開任務(wù)管理器(最近任務(wù)按鈕)包个。會(huì)發(fā)現(xiàn)最近任務(wù)中現(xiàn)實(shí)的應(yīng)用名為B應(yīng)用,展示的界面卻是A應(yīng)用的StandardActivity(因?yàn)槠湮挥赥ask棧頂)冤留。
-
Android 5.0 之后
- 同一應(yīng)用內(nèi)
與Android 5.0之前保持一致 - 跨應(yīng)用啟動(dòng)
經(jīng)檢驗(yàn)與Android5.0之前保持一致碧囊。Android6.0上也依然沒改變树灶。參考資料深入講解Android中Activity launchMode
內(nèi)容或許有誤 。
- 同一應(yīng)用內(nèi)
使用場景
standard這種啟動(dòng)模式適合于撰寫郵件Activity或者社交網(wǎng)絡(luò)消息發(fā)布Activity糯而。如果你想為每一個(gè)intent創(chuàng)建一個(gè)Activity處理天通,那么就是用standard這種模式。
SingleTop
棧頂復(fù)用模式. SingleTop其實(shí)和Standard幾乎一樣歧蒋,使用SingleTop的Activity也可以創(chuàng)建很多個(gè)實(shí)例土砂。唯一不同的就是州既,如果調(diào)用的目標(biāo)Activity已經(jīng)位于調(diào)用者的Task的棧頂谜洽,則不創(chuàng)建新實(shí)例,而是使用當(dāng)前的這個(gè)Activity實(shí)例吴叶,并調(diào)用這個(gè)實(shí)例的onNewIntent方法阐虚。
在singleTop這種模式下,我們需要處理應(yīng)用這個(gè)模式的Activity的onCreate和onNewIntent兩個(gè)方法蚌卤,確保邏輯正常实束。
TaskRecord{537925a8 #42 A com.zlq.lmt U 0}
Run #4: ActivityRecord{537e3114 com.zlq.lmt/.SingleTopActivity}
Run #3: ActivityRecord{537dfe7c com.zlq.lmt/.StandardActivity}
Run #2: ActivityRecord{53770808 com.zlq.lmt/.SingleTopActivity}
Run #1: ActivityRecord{53760908 com.zlq.lmt/.MainActivity}
棧頂無法像Standard模式一樣,同事存在兩個(gè)逊彭,但是整個(gè)Task列表中間隔存在多個(gè)是可以的咸灿。
SingleTask
棧內(nèi)復(fù)用模式.使用singleTask啟動(dòng)模式的Activity在一個(gè)應(yīng)用Task中只會(huì)存在一個(gè)實(shí)例。如果這個(gè)實(shí)例已經(jīng)存在侮叮,intent就會(huì)通過onNewIntent傳遞到這個(gè)Activity避矢,即多次調(diào)用不會(huì)創(chuàng)建新實(shí)例。否則新的Activity實(shí)例被創(chuàng)建囊榜。
情況包含以下幾種:
- 同一應(yīng)用內(nèi)
- 任務(wù)棧不存在, 初次啟動(dòng)SingleTask實(shí)例, 會(huì)創(chuàng)建任務(wù)棧和實(shí)例.
Google在singleTask的文檔有這樣一段描述:
- 任務(wù)棧不存在, 初次啟動(dòng)SingleTask實(shí)例, 會(huì)創(chuàng)建任務(wù)棧和實(shí)例.
The system creates a new task and instantiates the activity at the root of the new task.
意思為 系統(tǒng)會(huì)創(chuàng)建一個(gè)新的Task审胸,并創(chuàng)建Activity實(shí)例放入這個(gè)新的Task的底部。然而實(shí)際并非如此卸勺,在我的例子中砂沛,singleTask Activity并創(chuàng)建并放入了調(diào)用者所在的Task,而不是放入新的Task:
TaskRecord{5378ff88 #44 A com.zlq.lmt U 0}
Run #3: ActivityRecord{537e2ff0 com.zlq.lmt/.SingleTaskActivity}
Run #2: ActivityRecord{537de2ec com.zlq.lmt/.StandardActivity}
Run #1: ActivityRecord{53788be0 com.zlq.lmt/.MainActivity}
怎樣才能符合文檔中所描述的情況呢曙求?那就是 `taskAffinity`屬性和singleTask啟動(dòng)模式配合使用.
```xml
<activity
android:name=".SingleTaskWithTaskAffinityActivity"
android:label="SingleTaskWithTaskAffinityActivity"
android:launchMode="singleTask"
android:taskAffinity="com.zlq.new">
</activity>
```
此時(shí)再執(zhí)行同樣的操作碍庵,棧內(nèi)的情況:
TaskRecord{53778428 #45 A com.zlq.new U 0}
Run #3: ActivityRecord{537db410 com.zlq.lmt/.SingleTaskWithTaskAffinityActivity}
TaskRecord{5378ff88 #44 A com.zlq.lmt U 0}
Run #2: ActivityRecord{53760908 com.zlq.lmt/.StandardActivity}
Run #1: ActivityRecord{53788be0 com.zlq.lmt/.MainActivity}
其實(shí),**把啟動(dòng)模式設(shè)置為singleTask悟狱,framework在啟動(dòng)該activity時(shí)只會(huì)把它標(biāo)示為可在一個(gè)新任務(wù)中啟動(dòng)怎抛,至于是否在一個(gè)新任務(wù)中啟動(dòng),還要受其他條件的限制芽淡。**使用`taskAffinity`屬性會(huì)指定新的Activity所屬棧马绝,可與SingleTask配合使用, 對Standard模式無效.新任務(wù)棧是com.zlq.new.
- 任務(wù)棧存在, 初次啟動(dòng)SingleTask實(shí)例, Task棧中不存在singleTask Activity的實(shí)例。那么就需要?jiǎng)?chuàng)建這個(gè)Activity的實(shí)例挣菲,并且將這個(gè)實(shí)例放入和調(diào)用者相同的Task中并位于棧頂富稻。與Standard模式相同.
- 任務(wù)棧相同,如果singleTask Activity實(shí)例已然存在,再次啟動(dòng)SingleTask實(shí)例, 那么在Activity回退棧中掷邦,所有位于該Activity上面的Activity實(shí)例都將被銷毀掉(銷毀過程會(huì)調(diào)用Activity生命周期回調(diào)),這樣使得singleTask Activity實(shí)例位于棧頂(具有clearTop的效果)椭赋。與此同時(shí)抚岗,Intent會(huì)通過onNewIntent傳遞到這個(gè)SingleTask Activity實(shí)例。 并清除其上面實(shí)例, 具有clearTop的效果.最終哪怔,singleTask Activity實(shí)例會(huì)位于棧頂宣蔚。
- 任務(wù)棧不同, 再次啟動(dòng)SingleTask實(shí)例, 會(huì)導(dǎo)致任務(wù)棧切換, 后臺(tái)置于前臺(tái).
- 跨應(yīng)用之間:
- 任務(wù)棧不存在, 初次啟動(dòng)SingleTask實(shí)例, 會(huì)創(chuàng)建一個(gè)新的任務(wù)棧,然后創(chuàng)建SingleTask Activity的實(shí)例认境,將其放入新的Task中胚委。Task變化如下。
從
- 任務(wù)棧不存在, 初次啟動(dòng)SingleTask實(shí)例, 會(huì)創(chuàng)建一個(gè)新的任務(wù)棧,然后創(chuàng)建SingleTask Activity的實(shí)例认境,將其放入新的Task中胚委。Task變化如下。
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
Run #0: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
變?yōu)椋?/p>
TaskRecord{5c70a93 #17 A=com.zlq.lmt U=0 sz=1}
Run #1: ActivityRecord{4cd8b0f u0 com.zlq.lmt/.SingleTaskActivity t17}
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
Run #0: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
最近任務(wù)變化:
變?yōu)椋?/p>
- 任務(wù)棧存在, 初次啟動(dòng)SingleTask實(shí)例, Task棧中不存在singleTask Activity的實(shí)例叉信。
如果singleTask Activity所在的應(yīng)用進(jìn)程存在亩冬,但是singleTask Activity實(shí)例不存在,那么從別的應(yīng)用啟動(dòng)這個(gè)Activity硼身,新的Activity實(shí)例會(huì)被創(chuàng)建硅急,并放入到所屬進(jìn)程所在的Task中,并位于棧頂位置佳遂。
從
Running activities (most recent first):
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
Run #1: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=1}
Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}
↓變?yōu)椤?/p>
Running activities (most recent first):
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
Run #2: ActivityRecord{73d091c u0 com.zlq.lmt/.SingleTaskActivity t18}
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
Run #1: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}
- 如果singleTask Activity實(shí)例存在营袜,從其他程序被啟動(dòng),那么這個(gè)Activity所在的Task會(huì)被移到頂部丑罪,并且在這個(gè)Task中荚板,位于singleTask Activity實(shí)例之上的所有Activity將會(huì)被正常銷毀掉。如果我們按返回鍵巍糯,那么我們首先會(huì)回退到這個(gè)Task中的其他Activity啸驯,直到當(dāng)前Task的Activity回退棧為空時(shí),才會(huì)返回到調(diào)用者的Task祟峦。
從
Running activities (most recent first):
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
Run #4: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=4}
Run #3: ActivityRecord{fa7aae9 u0 com.zlq.lmt/.StandardActivity t18}
Run #2: ActivityRecord{dfd9a3b u0 com.zlq.lmt/.StandardActivity t18}
Run #1: ActivityRecord{660ce3c u0 com.zlq.lmt/.SingleTaskActivity t18}
Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}
↓變?yōu)椤?/p>
Running activities (most recent first):
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
Run #2: ActivityRecord{660ce3c u0 com.zlq.lmt/.SingleTaskActivity t18}
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
Run #1: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}
可以看到罚斗,TASK ID為#18
的任務(wù)棧已經(jīng)從原來的4
個(gè)變?yōu)樽罱K的1+1
個(gè)。
- 使用場景:
該模式的使用場景多類似于郵件客戶端的收件箱或者社交應(yīng)用的時(shí)間線Activity宅楞。上述兩種場景需要對應(yīng)的Activity只保持一個(gè)實(shí)例即可针姿,但是也要謹(jǐn)慎使用這種模式,因?yàn)樗梢栽谟脩粑锤兄那闆r下銷毀掉其他Activity厌衙。
SingleInstance
單實(shí)例模式啟動(dòng)時(shí), 系統(tǒng)會(huì)為其創(chuàng)造一個(gè)單獨(dú)的任務(wù)棧, 以后每次使用, 都會(huì)使用這個(gè)單例, 直到其被銷毀, 屬于真正的單例模式.singleTask差不多距淫,唯一不同的就是存放singleInstance Activity實(shí)例的Task只能存放一個(gè)該模式的Activity實(shí)例,不能有任何其他的Activity婶希。
雖然是兩個(gè)task榕暇,但是在系統(tǒng)的任務(wù)管理器中,卻始終顯示一個(gè),即位于頂部的Task中彤枢。
相關(guān)知識(shí)點(diǎn)
查看當(dāng)前任務(wù)棧:
adb shell dumpsys activity | sed -n -e '/Stack #/p' -e '/Running activities/,/Run #0/p'
輸出的結(jié)果如:
Running activities (most recent first):
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
Run #2: ActivityRecord{660ce3c u0 com.zlq.lmt/.SingleTaskActivity t18}
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
Run #1: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}
拿TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
為例
以TaskRecord開頭的(如)為一組 TaskRecord記錄狰晚,#18為Task的ID,A=包名缴啡,sz為該Task的Activity數(shù)量壁晒。
以Run #開頭的為一個(gè)ActivityRecord記錄。其中也包含了包名业栅、類名秒咐、TASK ID等信息。
其中以Task ID為一個(gè)任務(wù)棧的唯一標(biāo)識(shí)碘裕,ID相同的TaskRecord屬于同一個(gè)任務(wù)棧(可以理解為同一應(yīng)用)携取。
對startActivityForResult的影響:
startActivityForResult 不同于 startActivity, 在使用 startActivityForResult 時(shí)不管LaunchMode設(shè)置為哪種模式,都會(huì)在調(diào)用者Task棧中新建實(shí)例以正確地返回?cái)?shù)據(jù)娘汞。在棧中的展現(xiàn)形式均與Standard相同(可生成多份連續(xù)的實(shí)例)歹茶。
- SingleTop,當(dāng)其使用startActivityForResult時(shí)表現(xiàn)和Standard啟動(dòng)模式時(shí)完全相同
- SingleTask夕玩,當(dāng)不定義 taskAffinity 屬性時(shí)使用startActivityForResult和Standard啟動(dòng)模式時(shí)表現(xiàn)完全相同你弦。當(dāng)定義了taskAffinity 屬性后,變現(xiàn)將和下面第3條表現(xiàn)一致燎孟。
- SingleInstance禽作,無法通過startActivity創(chuàng)建自己(無論當(dāng)前所屬哪個(gè)棧)。startActivityForResult 隨意在當(dāng)前棧(傳入者所在棧)新建實(shí)例揩页。
Running activities (most recent first):
TaskRecord{ef9f20b #33 A=com.zlq.lmt U=0 sz=4}
Run #3: ActivityRecord{34f60b1 u0 com.zlq.lmt/.SingleTaskActivity t33} *
Run #2: ActivityRecord{914547d u0 com.zlq.lmt/.SingleTaskActivity t33} *
Run #1: ActivityRecord{a1f3e09 u0 com.zlq.lmt/.SingleTaskActivity t33}
Run #0: ActivityRecord{f7db1c u0 com.zlq.lmt/.MainActivity t33}
上面代碼片段中旷偿,加了*
標(biāo)的表示使用startActivity無法建立,是 使用startActivityForResult
建立的Activity爆侣。
由此可知, 因?yàn)閟tartActivityForResult需要返回值, 會(huì)保留實(shí)例, 部分覆蓋單例效果.
注意: 4.x版本通過startActivityForResult啟動(dòng)singleTask, 無法正常獲取返回值, 參考.
5.x以上版本修復(fù)此問題, 考慮兼容性, 不推薦使用startActivityForResult和singleTask.
Demo源碼
以上均為參考資料和自己實(shí)踐驗(yàn)證所得結(jié)果萍程。有描述不清楚的地方,大家可去下載我的代碼自行驗(yàn)證各種情況:GitHub
參考資料鏈接
深入講解Android中Activity launchMode
分析 Activity 的啟動(dòng)模式
Android中Activity四種啟動(dòng)模式和taskAffinity屬性詳解