參考
任務(wù)棧和Activity啟動模式
Android 面試黑洞——當我按下 Home 鍵再切回來少孝,會發(fā)生什么?
前言
之前在網(wǎng)上檢索一些lanchMode的知識继低,發(fā)現(xiàn)一點不言簡意賅,有些把實驗和結(jié)論混合一起的稍走,想法很好袁翁,但閱讀起來其實很混亂,理解起來也麻煩婿脸,沒有整體的框架邏輯去看這些的時候粱胜,好像學(xué)到了啥,其實整體來對比又一團混亂狐树,不體系焙压。
- 建議
本文就只參考了上面兩個連接,就完全理解lanchMode的整體框架,建議直接看完本篇以后涯曲,再去看這個Android 面試黑洞——當我按下 Home 鍵再切回來答憔,會發(fā)生什么?視頻,看看是否自己理解清楚了掀抹。理解了這個框架虐拓,再去關(guān)于Flag的,就都只是些策略問題了傲武。
以下是一些總結(jié)和問題蓉驹,可以看完以后再回看這些,看看自己是否是真的理解了
默認(standard)和singleTop:多用于App內(nèi)部
singleTask:內(nèi)部交互和外部交互都會用上揪利,singleInstance:多用于開放給外部App來共享使用
singeTop = standard + 約束
singleInstance = singTask + 約束
在最近任務(wù)里看見的Task不一定活著
在最近任務(wù)里看不見的Task不一定死了
singleTask流程态兴?
singleInstance怎么能做到獨占一個任務(wù)棧
預(yù)熱
在具體開始啟動模式相關(guān)知識的時候,首先需要搞清楚一下兩點
- Task任務(wù)棧
- taskAffinity
認清任務(wù)棧
首先一個簡單的關(guān)系圖:
- 任務(wù)棧結(jié)構(gòu)
任務(wù)棧疟位,最直觀的表現(xiàn)就是打開最近任務(wù)的時候瞻润,展示的一個個任務(wù),其實就是一個個task任務(wù)棧
安卓系統(tǒng)管理著不同模式下的多個ActivityStack(比如在home launcher界面需要有一個ActivityStack甜刻,畫中畫模式绍撞,分屏模式等)。
- 一個ActivityStack可以包含很多個TaskRecord得院。
- 一個TaskRecord又可以包含很多個ActivityRecord傻铣。
- 每一個ActivityRecord都會有一個Activity與之對應(yīng),一個Activity可能會有多個ActivityRecord祥绞,因為Activity可能被多次實例化非洲。
一系列相關(guān)的ActivityRecord組成了一個TaskRecord,TaskRecord是存在于ActivityStack中蜕径,ActivityStackSupervisor是用來管理這些ActivityStack的两踏。
launcher也有自己的task,
系統(tǒng)是根據(jù)task進行管理的兜喻,而不是ActivityStack梦染。
- 前后臺task
task又分為前臺task和后臺task,前臺task(也叫當前task)就是棧頂是和用戶交互的activity的task虹统,后臺task就是非前臺task弓坞。
Activity可以在Task內(nèi)部疊成棧,不同task之間也可以疊成棧车荔,不過只針對前臺task,前臺疊加的多個task渡冻,在進入后臺會立刻被拆開,這也就是造成"singleTask"模式下回退路徑變化的問題。
當前 Activity 啟動另一個 Activity 時忧便,該新 Activity 會被推送到堆棧頂部族吻,成為焦點所在帽借。 前一個 Activity 仍保留在堆棧中,但是處于停止狀態(tài)超歌。Activity 停止時砍艾,系統(tǒng)會保持其用戶界面的當前狀態(tài)。 用戶按"返回"按鈕時巍举,當前 Activity 會從堆棧頂部彈出(Activity 被銷毀)脆荷,而前一個 Activity 恢復(fù)執(zhí)行(恢復(fù)其 UI 的前一狀態(tài))。
如果用戶繼續(xù)按"返回"懊悯,堆棧中的相應(yīng) Activity 就會彈出蜓谋,以顯示前一個 Activity,直到 所有 Activity 均從堆棧中移除后炭分,任務(wù)即不復(fù)存在桃焕,回到桌面,但此時打開最近任務(wù)依然能看到該task的一個“殘影”捧毛。
taskAffinity
清單文件中Application和Activity標簽都可以使用"android:taskAffinity"標記
它代表這個Activity所希望歸屬的Task,也就是分組观堂,在默認情況下,同一個app中的所有Activity擁有共同的Affinity呀忧,即manifest中定義的package师痕。
一個Activity的taskAffinity的優(yōu)先級:activity設(shè)置的 -> Application設(shè)置的 -> packageName
taskAffinity與Task、Activity的關(guān)系
前面說了荐虐,taskAffinity其實就是一個分組標記七兜。
- Activty有"android:taskAffinity"可以更改標記,默認取自Application的標記福扬, Application默認又取自包名。
- 對于Task的affinity標記則取決于它的根部Activity惜犀。
多個task可以擁有相同的taskAffinity铛碑,但最近列表只會展示最新展示過的那一個task
taskAffinity在兩種情況下起作用:
知道taskAffinity是標記作用,就很好理解下面的情況了虽界。
- 當啟動Activity的Intent中帶有FLAG_ACTIVITY_NEW_TASK標志時汽烦。
在默認情況下,目標Activity將與startActivity的調(diào)用者處于同一task中莉御。但如果用戶特別指定了FLAG_ACTIVITY_NEW_TASK撇吞,表明它希望為Activity重新開設(shè)一個Task。這時就有兩種情況:
- 假如當前已經(jīng)有一個Task礁叔,它的affinity與新Activity是一樣的牍颈,那么系統(tǒng)會直接用此Task來完成操作,而不是另外創(chuàng)建一個Task琅关;
- 否則系統(tǒng)需要創(chuàng)建一個Task煮岁。
- 當Activity中的allowTaskReparenting屬性設(shè)置為true時。
在這種情況下,Activity具有"動態(tài)轉(zhuǎn)移"的能力画机。舉個前面的"短信"例子冶伞,在默認情況下,該應(yīng)用程序中的所有Activity具有相同的affinity步氏。
當另一個程序啟動了"短信編輯"時响禽,一開始這個Activity和啟動它的Activity處于同樣的Task中。但如果"短信編輯"Activity指定了allowTaskReparenting荚醒,且后期"短信"程序的Task轉(zhuǎn)為前臺金抡,此時"短信編輯"這一Activity會被"挪"到與它更親近的"短信"Task中。
注意:allowTaskReparenting在安卓9.0和10.0可能有問題
啟動模式
在Android中每個界面都是一個Activity腌且,切換界面操作其實是多個不同Activity之間的實例化操作梗肝。在Android中Activity的啟動模式?jīng)Q定了Activity的啟動運行方式。
有兩種方式來聲明指定啟動模式:
- 在清單文件中
- 在java代碼中
如果 Activity A 啟動 Activity B铺董,則 Activity B 可以在其清單文件中定義它應(yīng)該如何與當前任務(wù)關(guān)聯(lián)(如果可能)巫击,并且 Activity A 還可以請求 Activity B 應(yīng)該如何與當前任務(wù)關(guān)聯(lián)。
如果這兩個方式均定義 Activity B 應(yīng)該如何與任務(wù)關(guān)聯(lián)精续,則 Activity A 的請求(Intent 中所定義)優(yōu)先級要高于 Activity B 的請求(其清單文件中所定義)坝锰。
本篇只講在清單申明的情況
在清單文件中
不管何種方式啟動Activity,被啟動的Activity通常都要位于ActivityStack的棧頂重付。
Activity啟動方式launchMode顷级,在清單文件中配置。
<activity android:name=".MainActivity"
android:launchMode="xxx" />
您可以分配給 launchMode 屬性的啟動模式共有四種:
"standard"(默認模式)
- 概述:
和任務(wù)棧的taskAffinity沒有直接相關(guān)确垫,啟動一次弓颈,就會在當前任務(wù)棧直接新創(chuàng)建一個實例
也就是說Activity 可以多次實例化,而每個實例均可屬于不同的任務(wù)删掀,并且一個任務(wù)可以擁有多個該activity實例翔冀。
"singleTop"
- 概述:
和"standard"模式類似,只更當前任務(wù)棧相關(guān)披泪,唯一一點不同是纤子,當要啟動的Activity和當前任務(wù)棧棧頂?shù)腁ctivity一樣時,只調(diào)用該實例的 onNewIntent() 方法向其傳送 Intent款票,而不是創(chuàng)建 Activity 的新實例控硼。
例如,假設(shè)任務(wù)堆棧是 A-B-C-D艾少;D 位于頂部卡乾。收到針對 D 類 Activity 的 Intent,
- 如果 D 具有默認的 "standard" 啟動模式姆钉,則會啟動該類的新實例说订,且堆棧會變成 A-B-C-D-D抄瓦。
- 如果 D 的啟動模式是 "singleTop",則 D 的現(xiàn)有實例會通過 onNewIntent() 接收 Intent陶冷,因為它位于堆棧的頂部钙姊;而堆棧仍為 A-B-C-D。
- 如果收到針對 B 類 Activity 的 Intent埂伦,則會向堆棧添加 B 的新實例煞额,即便其啟動模式為 "singleTop" 也是如此。
"singleTask"
- 概述:
這種模式和taskAffinity息息相關(guān)沾谜,整體邏輯流程如下:
首先根據(jù)要啟動的Acitivity的taskAffinity找任務(wù)棧膊毁,分三種情況
- 已存在對應(yīng)taskAffinity的任務(wù)棧,而且就是當前任務(wù)棧
- 已存在對應(yīng)taskAffinity的任務(wù)棧基跑,不是當前任務(wù)棧
- 不存在對應(yīng)taskAffinity的任務(wù)棧婚温。則創(chuàng)建新的任務(wù)棧
注:這里2,3情況媳否,之后任務(wù)棧會疊成棧栅螟,這種方式只適用于前臺task然后在找到對應(yīng)任務(wù)棧以后,就會在棧里面找是否已經(jīng)有這個Activity了篱竭,分成兩種情況
a. 已經(jīng)有了力图,則把它彈到棧頂(意思是,它如果不在棧頂掺逼,就把現(xiàn)在棧頂?shù)囊粋€個彈出吃媒,直到它到棧頂為止),并且調(diào)用onNewIntent() 方法向其傳送 Intent吕喘,而不是創(chuàng)建新實例
b. 沒有的話,則創(chuàng)建新的實例壓棧
下圖這種就是 2a組合的情況:
對于這個模式赘那,有個回退路變更問題
- 回退路徑,上面提到了任務(wù)棧的疊加兽泄,因為在切換后臺的時候漓概,疊加任務(wù)棧會被立刻拆分,所以前臺task就只有一個了病梢,回退路徑就會發(fā)生變化。
這個時候可以考慮屬性allowTaskReparenting梁肿,希望創(chuàng)建新任務(wù)并實例化Activity蜓陌,當然會受到taskAffinity的影響,即如果已經(jīng)有一個和要啟動的Activity的taskAffinity相同的task吩蔑,那么就在這個task中創(chuàng)建實例钮热,相反就是創(chuàng)建一個新任務(wù)。并且會將這個任務(wù)棧壓到當前的任務(wù)棧
"singleInstance"
- 總結(jié):
這個模式最復(fù)雜烛芬,該模式下隧期,與 "singleTask" 類似飒责,只是條件更嚴苛一點。就是此activity獨占一個任務(wù)棧,而之所以能保證它自己一個獨占一個任務(wù)棧仆潮,是因為多個任務(wù)椇牝龋可以擁有同一個taskAffinity
一旦該模式的Activity的實例存在于某個棧中,任何應(yīng)用再激活該Activity時都會重用該棧中的實例性置,其效果相當于多個應(yīng)用程序共享一個應(yīng)用拾并,不管誰激活該Activity都會進入同一個應(yīng)用中。
singleTask/singleInstance的特別說明
從ActivityStack源碼可以看出鹏浅,singleTask/singleInstance模式會clearTop的效果嗅义,
} else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
從ActivityStack源碼可以看出,singleTask/singleInstance模式會自動添加FLAG_ACTIVITY_NEW_TASK
} else if (mLaunchSingleInstance || mLaunchSingleTask) {
// The activity being started is a single instance... it always
// gets launched into its own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
總結(jié)
這里面最好理解的就是standard和singleTop兩個模式了隐砸,而singleTask和singleInstance兩個模式必須先要理解 Task和takeInfinity這兩個概念之碗,然后搞清楚singleTask流程就行,singleInstance就只是加了一個獨占task的條件季希。
整體結(jié)構(gòu)就這些褪那,看完這個,再去看b站 拋物線的視頻胖眷,相信會很容易理解了武通,結(jié)構(gòu)也更加清晰了