Activity的launchMode和taskAffinity多实验对比

Android面试技术要点汇总
2021-05-17 14:29 · 阅读时长11分钟
小课

Activity有四种启动模式,分别是standardsingleTopsingleTasksingleInstance,如果不指定则默认是standard模式。

standard

如果没有指定启动模式的话,默认会使用standard模式,这种模式下启动Activity,无论任务栈中是否已经有同类型的Activity实例,系统都会创建一个新的实例。

singleTop

如果任务栈中已经有相同类型的Activity实例,并且位于栈顶,则不会重新创建新的实例,通过onNewIntent传递新的Intent,否则会创建新的Activity实例。

singleTask

如果任务栈中已经有相同类型的Activity实例,无论是否位于栈顶,都不会创建新的实例,通过onNewIntent传递新的Intent,如果不是位于栈顶,则将其示例顶部的Activity全部销毁,如果任务栈中没有则创建新的Activity实例。

singleInstance

如果任务栈中已经有相同类型的Activity实例,就不会创建新的实例,通过onNewIntent传递新的Intent,如果任务栈中没有则创建新的任务栈和实例,并且该任务栈只存放该Activity。

其实上面的说明还不够精确,因为除了launchMode会影响这些行为,还有FLAG_ACTIVITY_*和taskAffinity等参数也会影响,下面就用一些示例来验证下各种不同的情况启动Activity的行为,文章末尾会附上整个示例项目。

示例一

我们使用测试集合1来进行测试,test1、test2都是standard模式,首先按照test1->test2顺序依次启动,此时再启动test1,会发现任务栈变成

test1->test2->test1

完整的日志如下:

D/LaunchMode: taskId: 2349, name: Test1, ref: TestActivity1@94282aa onCreate
D/LaunchMode: taskId: 2349, name: Test2, ref: TestActivity2@8516e9 onCreate
D/LaunchMode: taskId: 2349, name: Test2, ref: TestActivity2@c1d0baf onCreate

可以看出,当前栈中已经存在test2了,但是还是创建了一个新的test2实例。

示例二

我们使用测试集合1来进行测试,test1、test2是standard模式,test3是singleTop模式,首先按照test1->test2顺序依次启动,此时启动test3,会发现任务栈变成

任务栈一:test1->test2->test3

此时再启动test3,发现任务栈仍然是

任务栈一:test1->test2->test3

此时再启动test2,发现任务栈变成

任务栈一:test1->test2->test3->test2

此时再启动test3,发现任务栈变成

任务栈一:test1->test2->test3(1)->test2->test3(2)

完整的日志如下:

D/LaunchMode: taskId: 2349, name: Test1, ref: TestActivity1@c227ac onCreate
D/LaunchMode: taskId: 2349, name: Test2, ref: TestActivity2@a0984cc onCreate
D/LaunchMode: taskId: 2349, name: Test3, ref: TestActivity3@7de23bb onCreate
D/LaunchMode: taskId: 2349, name: Test3, ref: TestActivity3@7de23bb onNewIntent
D/LaunchMode: taskId: 2349, name: Test2, ref: TestActivity2@9bcd30e onCreate
D/LaunchMode: taskId: 2349, name: Test3, ref: TestActivity3@20b885c onCreate

可以看出,当前栈中不存在test3时,会在当前任务栈中创建一个新的test3实例,当任务栈中已经有test3实例并且处于栈顶时,则不会再创建新的test3实例,如果任务栈中存在test3实例,但是不在栈顶时,仍然会创建新的test3实例。

示例三

我们使用测试集合1来进行测试,test1、test2是standard模式,test4是singleTask模式,首先按照test1->test2顺序依次启动,此时启动test4,会发现任务栈变成

任务栈一:test1->test2->test4

此时再启动test4,发现任务栈仍然是

任务栈一:test1->test2->test4

此时再启动test2,发现任务栈变成

任务栈一:test1->test2->test4->test2

此时再启动test4,发现任务栈变成

任务栈一:test1->test2->test4

完整的日志如下:

D/LaunchMode: taskId: 2350, name: Test1, ref: TestActivity1@5c9e97f onCreate
D/LaunchMode: taskId: 2350, name: Test2, ref: TestActivity2@99438d2 onCreate
D/LaunchMode: taskId: 2350, name: Test4, ref: TestActivity4@80f5269 onCreate
D/LaunchMode: taskId: 2350, name: Test4, ref: TestActivity4@80f5269 onNewIntent
D/LaunchMode: taskId: 2350, name: Test2, ref: TestActivity2@fb38e04 onCreate
D/LaunchMode: taskId: 2350, name: Test4, ref: TestActivity4@80f5269 onNewIntent
D/LaunchMode: taskId: 2350, name: Test2, ref: TestActivity2@fb38e04 onDestroy

可以看出,当前栈中不存在test4时,会在当前任务栈中创建一个新的test4实例,当任务栈中已经有test4实例,则不会再创建新的test4实例,如果test4不在栈顶,则会将栈顶的activity全部销毁。

示例四

我们使用测试集合1来进行测试,test1、test2是standard模式,test5是singleInstance模式,首先按照test1->test2顺序依次启动,此时启动test5,会发现任务栈变成

任务栈一:test1->test2

任务栈二:test5

test5是单独一个任务栈,此时再启动test1会发现任务栈变成

任务栈一:test1->test2->test1

任务栈二:test5

此时再启动test5会发现任务栈仍然是

任务栈一:test1->test2->test1

任务栈二:test5

完整的日志如下:

D/LaunchMode: taskId: 2354, name: Test1, ref: TestActivity1@89f7f7b onCreate
D/LaunchMode: taskId: 2354, name: Test2, ref: TestActivity2@3242971 onCreate
D/LaunchMode: taskId: 2355, name: Test5, ref: TestActivity5@dac8757 onCreate
D/LaunchMode: taskId: 2354, name: Test1, ref: TestActivity1@18df2ad onCreate
D/LaunchMode: taskId: 2355, name: Test5, ref: TestActivity5@dac8757 onNewIntent

可以看出,当test5实例不存在时,会新启动一个任务栈和test5实例,并且新的任务栈中只会存放test5,如果test5实例已经存在,则不会创建新的实例。

示例五

我们使用测试集合2来进行测试,test1、test2是standard模式,test3是singleTask模式,另外test3的taskAffinity设置为.Test3,首先按照test1->test2顺序依次启动,此时启动test3,会发现任务栈变成

任务栈一:test1->test2

任务栈二:test3

test3是单独一个任务栈,此时再启动test1会发现任务栈变成

任务栈一:test1->test2

任务栈二:test3->test1

此时再启动test3会发现任务栈会变成

任务栈一:test1->test2

任务栈二:test3

完整的日志如下:

D/LaunchMode: taskId: 2358, name: Test1, ref: TestActivity1@b53f903 onCreate
D/LaunchMode: taskId: 2358, name: Test2, ref: TestActivity2@1613a00 onCreate
D/LaunchMode: taskId: 2359, name: Test3, ref: TestActivity3@6c8315f onCreate
D/LaunchMode: taskId: 2359, name: Test1, ref: TestActivity1@aec8ff5 onCreate
D/LaunchMode: taskId: 2359, name: Test3, ref: TestActivity3@6c8315f onNewIntent
D/LaunchMode: taskId: 2359, name: Test1, ref: TestActivity1@aec8ff5 onDestroy

可以看出,当test3实例不存在时,会新启动一个任务栈和test3实例,这是因为我们为test3配置了taskAffinity=.Test3,它会去查找与taskAffinity关联的任务栈,没有找到,所以会创建一个新的任务栈并且与taskAffinity关联,然后将test3实例存放到这个任务栈中,如果已经存在则根据singleTask模式的规则处理。

示例六

我们使用测试集合3来进行测试,test1、test2、test3是standard模式,另外test3的taskAffinity设置为.Test3,首先按照test1->test2顺序依次启动,此时设置FLAG_ACTIVITY_NEW_TASK标志启动test3,会发现任务栈变成

任务栈一:test1->test2

任务栈二:test3

test3是单独一个任务栈,此时再启动test1会发现任务栈变成

任务栈一:test1->test2

任务栈二:test3->test1

此时再设置FLAG_ACTIVITY_NEW_TASK标志启动test3会发现任务栈没有变化

任务栈一:test1->test2

任务栈二:test3->test1

不仅任务栈没有变化,而且test3也没有恢复到栈顶来。

完整的日志如下:

D/LaunchMode: taskId: 2407, name: Test1, ref: TestActivity1@5c9e97f onCreate
D/LaunchMode: taskId: 2407, name: Test2, ref: TestActivity2@5b87a82 onCreate
D/LaunchMode: taskId: 2408, name: Test3, ref: TestActivity3@8323949 onCreate
D/LaunchMode: taskId: 2408, name: Test1, ref: TestActivity1@14ba6ef onCreate

可以看出,设置FLAG_ACTIVITY_NEW_TASK标志和singleTask模式启动还是有一些区别,在test3已经启动的条件下,再次通过设置FLAG_ACTIVITY_NEW_TASK标志启动test3,此时不仅不会把test3恢复到栈顶,甚至连onNewIntent也没回调。

文件大小修改时间
app
2022年03月31日
build.gradle
358 B 2022年03月30日
gradle/wrapper
2022年03月30日
gradle.properties
1 kB 2022年03月30日
gradlew
6 kB 2022年03月30日
gradlew.bat
3 kB 2022年03月30日
local.properties
437 B 2022年03月30日
settings.gradle
327 B 2022年03月30日
launchModetaskAffinityandroid