前言:前一篇文章介紹了Android的activity的生命週期方法,activity的啓動模式也是一個難點,是因爲各種啓動模式和標示位太容易混淆。爲了滿足不同的使用場景,我們必須分清這些啓動模式。這一篇文章,我們來了解一下Activity的啓動模式,也算是一個深入吧,寫的不好,請各位多擔待。
Activity的LanuchMode
在我們新建activity時,如果不選擇啓動模式,則默認使用的是standard模式,這種模式在使用過程中,如果我們多次啓動該activity,則會在棧裏重複創建多個activity實例,單擊back鍵時,則會一一回退,出棧。任務棧是一種“後進先出”的棧結構,意思就是新入棧的任務會放在棧頂,出棧的時候,由棧頂依次出棧。所以我們在使用activity時不可避免的會出現上述問題,導致棧內有多個重複的activity,這是不合理的,所以我們需要分清楚activity的啓動模式,在適合的場景下,使用合適的啓動模式,這就是我們的目的。首先我們先來看一下activity都有哪些啓動模式,然後我們通過實例來分別認識一下它們。啓動模式分別有以下四種模式:
- standard模式: 標準模式,也就是默認模式,每次啓動一個activity的時候,都會創建一個新的實例,不管這個實例是否存在。被創建的實例,生命週期都符合典型情況下的Activity生命週期。一個任務棧中可以有多個實例,每個實例也可以屬於不同的任務棧。在這種模式下,誰啓動了這個activity,那麼這個activity就會放在誰的任務棧中。當我們用AoolicationContext 去啓動standard模式的activity的時候,就會出現:
Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
這樣的異常。其實很好理解,就是因爲,非Activity類型的Context(如ApplicationContext) 並沒有所謂的任務棧,所以就會啓動失敗。解決方法就是爲啓動的activity指定FLAG_ACTIVITY_NEW_TASK標記位。這樣啓動這個activity的時候就會創建一個新的任務棧,並且將activity放進任務棧。此時activity其實是以singleTask模式啓動的。
具體實例如下:
//啓動自己
btn_start_myself.setOnClickListener {
var sIntent = Intent()
sIntent.setClass(MainActivity@this,MainActivity::class.java)
startActivity(sIntent)
}
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
點擊3次啓動自己,然後打開dos命令行,輸入adb shell dumpsys activity,找到如下位置,可以看到,Running activitys 中有4個activity,分別就是剛纔啓動的3個,再加上初始一個。所以使用該模式時,啓動請求都會創建新的activity實例,並放入棧內。
-
singleTop模式: 顧名思義,棧頂複用模式。在這種模式下,如果新activity已經位於棧頂,則新的activity不會被創建,同時會調用它的onNewIntent方法,通過此方法我們可以取出當前請求信息。此模式啓動的activity不會調用onCreate、onStart方法,因爲該activity沒有重建或者重新顯示。
示例如下:將上述的MainActivity的啓動模式改爲singleTop,然後多次點擊啓動自己,我們先看看生命週期回調日誌:
2019-02-16 22:59:02.165 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onPause(MainActivity.kt:43)
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onPause
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onNewIntent(MainActivity.kt:79)
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onNewIntent
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.168 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onResume(MainActivity.kt:61)
2019-02-16 22:59:02.168 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 22:59:02.168 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onResume
2019-02-16 22:59:02.168 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.373 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: Compiler allocated 6MB to compile void android.view.ViewRootImpl.performTraversals()
2019-02-16 22:59:20.825 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: Do full code cache collection, code=124KB, data=97KB
2019-02-16 22:59:20.827 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: After code cache collection, code=104KB, data=68KB
2019-02-16 23:00:03.094 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: Do partial code cache collection, code=121KB, data=91KB
2019-02-16 23:00:03.095 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: After code cache collection, code=121KB, data=91KB
2019-02-16 23:00:03.095 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: Increasing code cache capacity to 512KB
2019-02-16 23:00:33.628 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.628 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onPause(MainActivity.kt:43)
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onPause
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onNewIntent(MainActivity.kt:79)
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onNewIntent
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onResume(MainActivity.kt:61)
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onResume
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
然後我們在看看棧內activity的數量:
所以就算是多次點擊,棧內也還是隻有一個activity。
如果我們用另一個activity啓動singleTop模式的activity,看一下是否能複用。
SecondActivity部分代碼:
//啓動第一個activity
btn_start_1.setOnClickListener {
var sIntent = Intent()
sIntent.setClass(SecondActivity@this,MainActivity::class.java)
startActivity(sIntent)
}
將MainActivity的啓動模式改爲singleTop:
<activity android:name=".SecondActivity">
</activity>
<!--默認就是standard模式-->
<activity android:name=".MainActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
運行之後,在MainActivity中啓動SecondActivity,然後在SecondActivity中啓動MainActivity,此時我們再看棧內activity的情況:
可以看到此時棧內有3個activity,分別是兩個MainActivity和一個SecondActivity,由此說明singleTop只是在該activity在棧頂時才能複用。
-
singleTask模式:棧內複用模式。這是一種單實例模式,這種模式下,只要是棧內存在該activity實例,多次啓動後不會創建新的實例,而是會複用該activity,並且調用onNewIntent方法。例如:Activity A 是singleTask模式啓動的,再次啓動時,系統會先在棧內找是否存在A的實例的任務棧,如果不存在,則會創建新的任務棧,並將A的實例放在棧中,如果存在A所需的任務棧,則需要看該棧中是否存在A的實例,如果存在則直接調用onNewIntent方法,如果不存在,則創建A的實例。
還是上述兩個activity,但是我們修改一下MainActivity的啓動模式,並且操作步驟還是上述步驟,我們看一下棧內activity的情況:
此時我們可以看到棧內只有一個activity,這就是singleTask的另一個特性,就是說如果singleTask模式的activity在棧內存在,並且在它之上還有別的activity,則再次啓動這個activity的時候,在它之上的所有activity全部出棧,然後直到該activity位於棧頂截止。
-
singleInstance模式:但是蘿莉模式。這是一種加強的singleTeask模式。此模式的activity只會單獨存在以一個任務棧中。例如:activity A 是以singleInstance模式,當A 啓動後,系統會爲它創建一個單獨的任務棧,並將A放入棧中,後續的請求不會再創建新的A,除非這個任務棧被銷燬。
我們將上述MainActivity的啓動模式改爲singleInstance,然後由MainActivity啓動SecondActivity,然後由SecondActivity啓動MainActivity,此時查看棧內情況如下:
我們會發現一個問題,棧內現在只有兩個activity實例,而SecondActivity啓動模式爲standard,照理說,應該存在多個activity的,但是爲什麼只有兩個呢?我們來看一下生命週期日誌:
2019-02-17 00:23:34.490 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.491 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onPause(MainActivity.kt:42)
2019-02-17 00:23:34.491 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.491 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onPause
2019-02-17 00:23:34.491 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.539 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ SecondActivity.onRestart(SecondActivity.kt:57)
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onRestart
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ SecondActivity.onStart(SecondActivity.kt:39)
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onStart
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.542 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ SecondActivity.onResume(SecondActivity.kt:45)
2019-02-17 00:23:34.542 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.542 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onResume
2019-02-17 00:23:34.542 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.586 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.587 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onSaveInstanceState(MainActivity.kt:84)
2019-02-17 00:23:34.587 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.587 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onSaveInstanceState
2019-02-17 00:23:34.587 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onStop(MainActivity.kt:66)
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onStop
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
由MainActivity再次啓動SecondActivity時,沒有重新創建activity,只是重新顯示出來,調用了onRestart方法。這個問題後續研究。
未完待續。。。。。