歡迎訪問我的個(gè)人博客 傳送門
10年積累的成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、外貿(mào)營銷網(wǎng)站建設(shè)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有大安免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
應(yīng)用通常包含多個(gè) Activity ,每個(gè) Activity 均應(yīng)圍繞用戶可以執(zhí)行的特定操作設(shè)計(jì),并且能夠啟動(dòng)其他 Activity,一個(gè) Activity 可以啟動(dòng)設(shè)備上其他應(yīng)用中的 Activity,即使兩個(gè) Activity 可能來自不同的應(yīng)用,但是 Android 仍會(huì)將 Activity 保留在相同的任務(wù)中,以維護(hù)這種無縫的用戶體驗(yàn)。這里所說的任務(wù)
就是指在執(zhí)行特定作業(yè)時(shí)與用戶交互的一系列 Activity,這些 Activity 按照各自的打開順序排列在堆棧
(即返回棧)中。返回棧
以“后進(jìn)先出”對(duì)象結(jié)構(gòu)運(yùn)行,如下圖
如果要查看 Activity Task棧的情況,可以在命令行用 adb 命令查看
adb shell dumpsys activity activities
執(zhí)行命令會(huì)出現(xiàn)很長一段詳細(xì)信息 找到 Running activities
即可查看,如下圖
在了解了任務(wù)和返回棧后,我們來說說啟動(dòng)模式,上圖的堆棧是比較常規(guī)的,如果我們一直啟動(dòng)同一個(gè) Activity 系統(tǒng)會(huì)重復(fù)創(chuàng)建多個(gè)實(shí)例,但這不是我們想要的結(jié)果。這時(shí)候?yàn)榱藵M足我們的需求就需要使用 Android 提供的啟動(dòng)模式來修改系統(tǒng)的默認(rèn)行為。目前有四種啟動(dòng)模式:standard、singleTop、singleTask 和 singleInstance。在 AndroidManifest.xml 中配置即可,如下:
系統(tǒng)在啟動(dòng) Activity 的任務(wù)中創(chuàng)建 Activity 的新實(shí)例并向其傳送 Intent。Activity 可以多次實(shí)例化,不管這個(gè)實(shí)例是否已經(jīng)存在,而每個(gè)實(shí)例均可屬于不同的任務(wù),并且一個(gè)任務(wù)可以擁有多個(gè)實(shí)例。這種模式的 Activity 被創(chuàng)建時(shí)它的 onCreate、onStart 都會(huì)被調(diào)用。這是一種典型的多實(shí)例實(shí)現(xiàn),一個(gè)任務(wù)棧中可以有多個(gè)實(shí)例,每個(gè)實(shí)例也可以屬于不同的任務(wù)棧。在這種模式下,誰啟動(dòng)了這個(gè) Activity,那么這個(gè) Activity 就運(yùn)行在啟動(dòng)它的那個(gè) Activity 所在的棧中。
這里通過簡單的代碼來驗(yàn)證,先實(shí)現(xiàn)方法來打印 Activity 的生命周期調(diào)用過程和 Taskid
fun printTaskInfo(activity: Activity, methodName: String) {
log("${activity.localClassName} $methodName taskId = ${activity.taskId}")
}
fun log(message: String, tag: String = "debugLog") {
Log.i(tag, message)
}
/**
* @param T 目標(biāo) Activity
*/
inline fun Context.toActivity() {
startActivity(Intent(this, T::class.java))
}
界面很簡單就一個(gè)按鈕,就不截圖了,Activity 代碼
class A : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_a)
printTaskInfo(this,"onCreate")
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
printTaskInfo(this,"onNewIntent")
}
override fun onStart() {
super.onStart()
printTaskInfo(this,"onStart")
}
fun click(view: View?) {
toActivity()
}
}
啟動(dòng) A 然后點(diǎn)擊兩下按鈕,日志如下:
01-02 22:07:00.330 28281-28281/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 44
01-02 22:07:00.332 28281-28281/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 44
01-02 22:07:01.580 28281-28281/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 44
01-02 22:07:01.582 28281-28281/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 44
01-02 22:07:02.325 28281-28281/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 44
01-02 22:07:02.327 28281-28281/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 44
使用 adb 命令查看 Activity Task 棧,可以看出每啟動(dòng)一次 A 都會(huì)創(chuàng)建一次實(shí)例,不管這個(gè)實(shí)例是否已經(jīng)存在
在這種模式下,如果當(dāng)前任務(wù)的頂部已存在 Activity 的一個(gè)實(shí)例,則系統(tǒng)會(huì)通過調(diào)用該實(shí)例的 onNewIntent() 方法向其傳送 Intent,而不是創(chuàng)建 Activity 的新實(shí)例。Activity 可以多次實(shí)例化,而每個(gè)實(shí)例均可屬于不同的任務(wù),并且一個(gè)任務(wù)可以擁有多個(gè)實(shí)例(但前提是位于返回棧頂部的 Activity 并不是 Activity 的現(xiàn)有實(shí)例)。這個(gè) Activity 的 onCreate、onStart 不會(huì)被系統(tǒng)調(diào)用,因?yàn)樗]有發(fā)生改變。
這里我們新建一個(gè) Activity B ,調(diào)用 Activity 流程 :A - A - B - A
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_a)
printTaskInfo(this, "onCreate")
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
printTaskInfo(this, "onNewIntent")
}
override fun onStart() {
super.onStart()
printTaskInfo(this, "onStart")
}
fun click(view: View?) {
when (view?.id) {
R.id.bt_toA -> toActivity()
R.id.bt_toB -> toActivity()
else -> { }
}
}
打印日志:
01-02 22:15:18.399 28530-28530/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 45
01-02 22:15:18.400 28530-28530/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 45
01-02 22:15:21.229 28530-28530/com.will.testdemo I/debugLog: launchmode.A onNewIntent taskId = 45
01-02 22:15:24.927 28530-28530/com.will.testdemo I/debugLog: launchmode.B onCreate taskId = 45
01-02 22:15:24.929 28530-28530/com.will.testdemo I/debugLog: launchmode.B onStart taskId = 45
01-02 22:15:26.449 28530-28530/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 45
01-02 22:15:26.450 28530-28530/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 45
Activity Task 棧
可以看出當(dāng) A 在當(dāng)前棧頂?shù)臅r(shí)候沒有創(chuàng)建新的實(shí)例,并調(diào)用 onNewIntent 方法,沒有調(diào)用 onCreate 和 onStart 方法
這是一種單實(shí)例模式,在這種模式下,只要 Activity 在一個(gè)棧中存在,那么多次啟動(dòng)此 Activity 都不會(huì)重新創(chuàng)建實(shí)例,和 singleTop一樣,系統(tǒng)也會(huì)回調(diào)其 onNewIntent。當(dāng)一個(gè)具有 singleTask 模式的Activity請(qǐng)求啟動(dòng)后,比如 Activity A,系統(tǒng)首先會(huì)尋找是否存在 A 想要的任務(wù)棧
,如果不存在,就重新創(chuàng)建一個(gè)任務(wù)棧,然后創(chuàng)建 A 的實(shí)例后把 A 放到棧中。如果存在 A 所需的任務(wù)棧,這時(shí)要看 A 是否在棧中有實(shí)例存在,如果有實(shí)例存在,那么系統(tǒng)就會(huì)把 A 調(diào)到棧頂并調(diào)用它的 onNewIntent 方法,如果實(shí)例不存在,就創(chuàng)建 A 的實(shí)例并把 A 壓入棧中 。
關(guān)于上文中所說的想要的任務(wù)棧
,指的是 taskAffinity 屬性,手動(dòng)設(shè)置所需的任務(wù)棧,這個(gè)后面會(huì)具體介紹
調(diào)用 Activity 流程 依舊是:A - A - B - A
打印日志:
01-02 22:25:59.608 24498-24498/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 54
01-02 22:25:59.611 24498-24498/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 54
01-02 22:26:02.844 24498-24498/com.will.testdemo I/debugLog: launchmode.A onNewIntent taskId = 54
01-02 22:26:05.753 24498-24498/com.will.testdemo I/debugLog: launchmode.B onCreate taskId = 54
01-02 22:26:05.758 24498-24498/com.will.testdemo I/debugLog: launchmode.B onStart taskId = 54
01-02 22:26:07.040 24498-24498/com.will.testdemo I/debugLog: launchmode.A onNewIntent taskId = 54
01-02 22:26:07.047 24498-24498/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 54
Activity Task 棧
納尼 Activity B 呢 怎么不見了,這是什么鬼操作,原來是 singleTask 默認(rèn)有 clearTop 的效果,會(huì)導(dǎo)致棧內(nèi)所有在它上面的 Activity 全部出棧
,這點(diǎn)一定不要忽略了
與 singleTask 相同,只是系統(tǒng)不會(huì)將任何其他 Activity 啟動(dòng)到包含實(shí)例的任務(wù)中。該 Activity 始終是其任務(wù)唯一僅有的成員;由此 Activity 啟動(dòng)的任何 Activity 均在單獨(dú)的任務(wù)中打開。也就是有此種模式的 Activity 只能單獨(dú)地位于一個(gè)任務(wù)棧中
調(diào)用 Activity 流程 :A - B - B - A
,這次把 B 的啟動(dòng)模式設(shè)置為 singleInstance
打印日志:
01-02 22:41:59.069 305-305/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 57
01-02 22:41:59.071 305-305/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 57
01-02 22:42:00.280 305-305/com.will.testdemo I/debugLog: launchmode.B onCreate taskId = 58
01-02 22:42:00.283 305-305/com.will.testdemo I/debugLog: launchmode.B onStart taskId = 58
01-02 22:42:02.340 305-305/com.will.testdemo I/debugLog: launchmode.B onNewIntent taskId = 58
01-02 22:42:03.658 305-305/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 57
01-02 22:42:03.659 305-305/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 57
Activity Task 棧
結(jié)合打印日志和 Activity Task 棧可以看出,有此種模式的 Activity 只能單獨(dú)地位于一個(gè)任務(wù)棧中,如果已經(jīng)創(chuàng)建過,則調(diào)用 onNewIntent 方法 不會(huì)調(diào)用 onCreate 和 onStart
taskAffinity,可以翻譯為任務(wù)相關(guān)性。這個(gè)參數(shù)標(biāo)識(shí)了一個(gè) Activity 所需要的任務(wù)棧的名字,默認(rèn)情況下,所有 Activity 所需的任務(wù)棧的名字為應(yīng)用的包名,當(dāng) Activity 設(shè)置了 taskAffinity 屬性,那么這個(gè) Activity 在被創(chuàng)建時(shí)就會(huì)運(yùn)行在和 taskAffinity 名字相同的任務(wù)棧中,如果沒有,則新建 taskAffinity 指定的任務(wù)棧,并將 Activity 放入該棧中。另外,taskAffinity 屬性主要和 singleTask 或者 allowTaskReparenting 屬性配對(duì)使用,在其他情況下沒有意義。
與 singleTask 結(jié)合使用,調(diào)用 Activity 流程:A - B - B - A - B
,設(shè)置 B 的啟動(dòng)模式為 singleTask ,并設(shè)置 taskAffinity
打印日志:
01-02 23:13:29.179 16793-16793/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 59
01-02 23:13:29.180 16793-16793/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 59
01-02 23:13:31.800 16793-16793/com.will.testdemo I/debugLog: launchmode.B onCreate taskId = 60
01-02 23:13:31.801 16793-16793/com.will.testdemo I/debugLog: launchmode.B onStart taskId = 60
01-02 23:13:33.740 16793-16793/com.will.testdemo I/debugLog: launchmode.B onNewIntent taskId = 60
01-02 23:13:34.928 16793-16793/com.will.testdemo I/debugLog: launchmode.A onCreate taskId = 60
01-02 23:13:34.931 16793-16793/com.will.testdemo I/debugLog: launchmode.A onStart taskId = 60
01-02 23:13:36.203 16793-16793/com.will.testdemo I/debugLog: launchmode.B onNewIntent taskId = 60
01-02 23:13:36.204 16793-16793/com.will.testdemo I/debugLog: launchmode.B onStart taskId = 60
Activity Task 棧
B 被創(chuàng)建時(shí),因沒有 com.will.testdemo.task1 的任務(wù)棧,于是新建任務(wù)棧,并把 B 放入棧內(nèi)。繼續(xù)創(chuàng)建 A,由于 A 沒有設(shè)置啟動(dòng)模式,則放入 com.will.testdemo.task1 棧中。再一次啟動(dòng) B,因棧內(nèi)有 B 實(shí)例,所以系統(tǒng)就把 B 調(diào)到棧頂,由于 singleTask 默認(rèn)有 clearTop 的效果,導(dǎo)致棧內(nèi)所有在它上面的 Activity 全部出棧,所以最后 com.will.testdemo.task1 棧內(nèi)只有 B 一個(gè)實(shí)例
上面廢話了那么多,那么這些啟動(dòng)模式到底什么時(shí)候使用呢,這里列出部分使用場景以供參考。
launchMode | 使用場景 |
---|---|
singleTop | 適合啟動(dòng)同類型的 Activity,例如接收通知啟動(dòng)的內(nèi)容顯示頁面 |
singleTask | 適合作為程序入口 |
singleInstance | 適合需要與程序分離開的頁面,例如鬧鈴的響鈴界面 |