Android启动模式和TaskAffinity

任务和返回栈

应用通常包含多个 Activity ,每个 Activity 均应围绕用户可以执行的特定操作设计,并且能够启动其他 Activity,一个 Activity 可以启动设备上其他应用中的 Activity,即使两个 Activity 可能来自不同的应用,但是 Android 仍会将 Activity 保留在相同的任务中,以维护这种无缝的用户体验。这里所说的任务就是指在执行特定作业时与用户交互的一系列 Activity,这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。返回栈以“后进先出”对象结构运行。

查看activity Task栈的情况 adb命令:

adb shell dumpsys activity activities

启动模式

一直启动activity系统会创建多个实例,提供提供启动模式来修改系统的默认行为

1
2
3
4
5
6
7
adnroid:launchMode="standard" or "1"

"singleTop" or "2"

"singleTask" or "3"

"singleInstance" or "4"

standard默认模式

系统每启动一次 Activity 的任务就会创建一次 Activity 的新实例并向其传送 Intent。每个实例可以属于不同的任务,一个任务也能拥有多个实例。这种模式的 Activity 被创建时它的 onCreate、onStart 都会被调用。

singleTop栈顶复用模式

如果当前任务的顶部已存在Activity的一个实例,则系统会通过调用该实例onNewIntent()方法向其传送Intent,而不是创建Activity的新实例。当返回栈顶部不是所需Activity的现有实例,Activity可以被多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。这个 Activity 的 onCreate、onStart 不会被系统调用,因为它并没有发生改变。

singleTask 栈内复用模式

单例模式下,只要Acivity在一个栈中存在,那么多次启动次Activity都不会创建新实例,但是会通过回调onNewIntent来启动。

当具有一个singleTask模式的Activity请求启动后,比如Activity A,系统会先寻找是否存在A想要的任务栈taskAffinity属性),如果不存在,就重新创建一个任务栈,然后创建 A 的实例后把 A 放到栈中。如果存在 A 所需的任务栈,这时要看 A 是否在栈中有实例存在,如果有实例存在,那么系统就会把 A 调到栈顶并调用它的 onNewIntent 方法,如果实例不存在,就创建 A 的实例并把 A 压入栈中 。

关于上文中所说的想要的任务栈,指的是 taskAffinity 属性。

singleTask 默认有 clearTop 的效果,会导致栈内所有在它上面的 Activity 全部出栈

singleInstance单实例模式

与 singleTask 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其任务唯一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开。也就是有此种模式的 Activity 只能单独地位于一个任务栈中

如果已经创建过,则调用 onNewIntent 方法 不会调用 onCreate 和 onStart

taskAffinity属性

taskAffinity 属性主要和 singleTask 或者 allowTaskReparenting 属性配对使用,在其他情况下没有意义。

当设置了taskAffinity属性那么这个 Activity 在被创建时就会运行在和 taskAffinity 名字相同的任务栈中,如果没有,则新建 taskAffinity 指定的任务栈,并将 Activity 放入该栈中。

image-20201208161046801

总结

launchMode 使用场景
singleTop 适合启动同类型的 Activity,例如接收通知启动的内容显示页面
singleTask 适合作为程序入口
singleInstance 适合需要与程序分离开的页面,例如闹铃的响铃界面

onNewIntent()方法

当Acivity已经被启动且处于当前应用返回栈中,activity的LaunchMode为SingleTop、SingleTask、SingleInstance时会被调用。

  • 当Acivityde LaunchMode为singleTop时

    当需要再次响应次Activity启动需求时,会复用栈顶的已有的Activity,还会调用onNewIntent()方法,并且,再次接受新发来的intent()一定会先执行onPause()方法。

    生命周期顺序:onCreate->onStart->onResume->onPause->onNewIntent->onResume

  • 当Activity的LaunchMode为SingleTask,SingleInstance

    生命周期调用顺序:onCreate -> onStart -> onResume -> …… -> onPause -> onStop -> onNewIntent -> onRestart -> onStart -> onResume

onNewIntent()中的陷阱:

当多次启动一个栈唯一模式下的activity时,在onNewIntent()里面的getIntent()得到的intent感觉都是第一次的那个数据。

这儿会有个陷阱,因为它就是会返回第一个intent的数据,就是这个坑。

原因是因为没有onNewIntent()里面设置setIntent(),即将最新intent设置在这个activity实例中。

1
2
3
4
5
6
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);//设置新的intent
int data = getIntent().getIntExtra("tanksu", 0);//此时的到的数据就是正确的了
}

参考链接:https://www.cnblogs.com/pshell/p/7979649.html

https://www.jianshu.com/p/2c73be80ce8d

https://blog.csdn.net/goodlixueyong/article/details/49620667

https://juejin.cn/post/6844904007085850631