1.Activity的启动模式
当我们多次调用同一个Activity时,系统会重复创建多个实例并把它们一一放入任务栈中,这种方式显然不符合我们的设计要求。所以Android在设计时就提供了四种启动模式来解决此问题。
四种启动模式分别如下:
- standard-标准模式也是默认模式
每次启动一个Activity都会创建新的实例并压入任务栈,onCreate,onStart,onResume都会被调用。每个任务栈可以有多个实例,每个实例也可属于不同的任务栈。例如:
A->B,B为standard模式,B即属于A的任务栈。
A B C D 再次启动A(standard模式)
当前任务栈存在的元素 | 启动后的任务栈存在的元素 |
---|---|
A B C D | A B C D A |
- singleTop-栈顶复用模式
此模式下,比如新的Activity已经位于栈顶,那么此Activity不会被重新创建,会回调它的onNewIntent方法,它的onCreate,onStart不会被系统调用。如果新的Activity不是位于栈顶,那么它还是会被重新创建。例:
A B C D 再次启动D(singleTop模式)
A B D C 再次启动D(singleTop模式)
当前任务栈存在的元素 | 启动后的任务栈存在的元素 |
---|---|
A B C D | A B C D |
A B D D | A B D C D |
- singleTask-栈内复用模式
单实例模式,只要Activity在一个栈中存在,多次启动都不会重新创建实例,系统会回调onNewIntent方法。
如果要启动一个启动模式为singleTask的Activity A,先判断是否存在A所需的任务栈,不存在->新创建任务栈并把实例A压入。如果存在,判断A是否在栈中存在实例,存在移到栈顶,并将之前的实例出栈,如果不存在实例,创建并压入栈。
任务栈S1存在A B C,启动D(singleTask模式)(所需任务栈S2),因为S2与D实例均不存在,所以系统会新建任务栈S2并创建D压入S2。
任务栈S1存在A B C,启动D(singleTask模式)(所需任务栈S1),启动D后的S1为A B C D。
任务栈S1存在A D B C,启动D(singleTask模式)(所需任务栈S1),启动D后的S1为A D。
使用:
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.d(TAG, "--onNewIntent--")
}
可以看出MainActivity始终没有被重新创建
- singleInstance-单实例模式
加强的singleTask模式,拥有singleTask的所有特点且只能单独的位于一个任务栈中。
特殊情况
?:现在有S1-前台任务栈,S2-后台任务栈(singleTask模式)。S1有A B,S2有C D。
现在启动D,S2整个任务栈会切换至S1中,即S1存在 A B C D,依此按下返回键实例一一出栈。
现在启动C,S2整个任务栈会切换至S1中,即S1存在 A B C,依此按下返回键实例一一出栈。
2.任务栈
前面所说的Activity所需任务栈是什么?即TaskAffinity参数:这个参数标识了一个Activity所需任务栈的包名。此属性主要与singleTask启动模式或者allowReparenting属性配对使用。
1.当与singleTask配对使用时,启动的Activity会运行在名字与TaskAffinity相同的任务栈中。
2.当与allowReparenting配对使用时,情况比较特殊。
现有应用A与B(存在Activity C),现在A启动B的C,点击Home键。点击B 会启动C。--C从A的任务栈转移到了B的任务栈。
3.当启动模式为Standard以及SingleTop时指定TaskAffinity属性不生效
简单使用:
A为standrad
B为singleTask TaskAffinity值为”com.wdl.test“。模拟过程A->B->A
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Main2Activity"
android:taskAffinity="com.wdl.test"
android:launchMode="singleTask"/>
控制台输入adb shell dumpsys activity
发现此时存在两个任务栈 一个为包含B A的S1(com.wdl.test),另一个为只有A的S2 (com.wdl.kt1)
分为2类:
- 前台任务栈
- 后台任务栈,位于暂停状态
3.如何给Activity指定启动模式
- 通过AndroidMenifest.xml为Activity指定启动模式
<activity android:name=".Main2Activity"
android:launchMode="singleTask"/>
- 在Intent中设置标志位为Activity指定启动模式
val intent = Intent(this,MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
常用的标志位有:
值 | 作用 |
---|---|
FLAG_ACTIVITY_NEW_TASK | 指定singleTask启动模式 |
FLAG_ACTIVITY_SINGLE_TOP | 指定singleTop启动模式 |
FLAG_ACTIVITY_CLEAR_TOP | 此Activity(singleTask)之前的实例全部要出栈; Activity(standard)之前与本身的实例全部要出栈,重新创建实例并入栈 |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 具有此标记的Activity不会出现在历史Activity中 |
二者的区别:
1.优先级上第二种方式高于第一种方式。
2.限定范围上有所不同
例:第一种方式无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识;第二种方式无法为Activity指定singleInstance模式
案例一:
有三个activity
A(singleInstance) B(singleTop) C(Standard)
启动顺序:
C -> B -> A ->B ,退出时的界面,以及任务栈的个数,back键的回退情况如何?
配置文件:
<activity android:name=".CActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".BActivity"
android:launchMode="singleTop" />
<activity
android:name=".AActivity"
android:launchMode="singleInstance"
android:taskAffinity="com.wdl.demo"/>
直接上图吧:
任务栈如下:
详细分析:
压栈过程:
C:S1(C)前台任务栈
C->B:S1(C,B)前台任务栈
C->B->A:S2(A)前台任务栈,S1(C,B)后台任务栈
C->B->A->B:S2(A)后台任务栈,S1(C,B)前台任务栈
出栈过程:
一次Back键:S2(A)后台任务栈,S1(C)前台任务栈 B出栈
一次Back键:S2(A)前台任务栈 C出栈
一次Back键:finish A出栈