目录
Background
最近在项目中报出的UIWDT问题,重复出现了很多次由同一个非系统预置应用导致的UIWDT和系统卡顿,原因为其因为其启动模式为Standard,在运行中创建了大量的Activity,而在其代码中可能存在不恰当的引用,导致Activity无法正确被回收。当系统资源耗尽时,会杀掉进程,但是当其被杀掉时,系统还需要处理其遗留的大量WINDEATH,进而引发UIWDT。**可见,选择一个适合的启动模式不仅对应用本身有帮助,还会影响到整个系统的性能和稳定性。**
在与其开发人员进行沟通时,问到我应该选择什么启动模式,我只是推荐了用SingleTask,然后用之前学过的概念把四种模式给对方说了一下,但是当问及SingleTask和SingleInstance的区别时,我发现我也不太好把这个区别用通俗的语言描述出来,所以准备再梳理和理解一下这个知识点。
Concept
首先,谷歌官方的开发者文档中原话是这么描述的:
Use Cases | LaunchMode | Multiple Instances | Comments |
---|---|---|---|
Normal launches for most activities | "standard" | Yes | Default. The system always creates a new instance of the activity in the target task and routes the intent to it. |
"singleTop" | Conditionally | If an instance of the activity already exists at the top of the target task, the system routes the intent to that instance through a call to its onNewIntent() method, rather than creating a new instance of the activity. | |
Specialized launches (not recommended for general use) | "singleTask" | No | The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one. |
"singleInstance" | No | Same as "singleTask", except that the system doesn't launch any other activities into the task holding the instance. The activity is always the single and only member of its task. |
翻译一下,也就是:
用例 | 启动模式 | 多实例 | 评论 |
适用大多数Activity | "standard" | 是 | 默认值,系统总是创建一个目标活动的新实例并将Intent导向它。 |
"singleTop" | 有条件的 | 如果目标活动已经存在任务栈顶,系统将调用onNewIntent()将Intent导向它,而不是创建新实例。 | |
特殊启动用途(不作通用推荐) | "singleTask" | 否 | 如果在当前任务栈中没有目标活动,则在栈底创建目标活动实例,否则系统通过调用onNewIntent()将Intent导向已有实例,而不是创建。 |
"singleInstance" | 否 | 和 “singleTask” 一样,除了如果当前任务栈已有目标活动的实例,系统不会再将其他活动的实例添加至该任务栈。该任务栈有且只有目标活动的实例。 |
Analyze
首先,我们需要简单理解一下什么是任务栈,在一个Application中,通常不仅只有一个Activity,而且这些Activity之间有时候需要互相调用和启动,而这些Activity,往往被存放在一个叫做Task的任务栈中,这个栈满足后进先出顺序。
1.standard
举例,在ActivityA中放一个startActivityB的按钮A,在ActivtyB中套娃再放一个startActivityB的按钮B,点击后会在当前的任务栈中创建2个ActivityB的实例,此时,在任务栈中就从上至下依次存在ActivityB@2、ActivityB@1、ActivityA,其中ActivityB@1、ActivityA处于后台。
2.singleTop(栈顶复用)
如果目标Activity恰好位于当前任务栈的栈顶,则不需要创建新的实例,否则就会再创建一个新的实例。比如有一个用于接受广播显示数据的Activity位于栈顶,通过这种方式我们不需要每次收到不同数据类型都启动一个新的Activity,而是直接修改显示的数据内容就可以了。
3.singleTask(栈内复用)
当任务栈中已存在目标ActivityA时,不会再创建新实例,而是调用已存在实例的onNewIntent()将其放到栈顶,并把在其栈上方的Activity全部pop出栈。比如浏览器主页,应用的主界面,当跳转回主页或者主界面,在其上方的子界面就销毁了。
4.singleInstance(单例)
这个模式理解起来稍微要复杂一些,按照官方的说法,singleInstance实际上是一种加强版的singleTask,但是区别在于,标识为singleTask的Activity可以存在于多个任务栈中,而且一个任务栈中可以存在多个Activity。但是对于singleInstance,首先,它只能存在于一个任务栈中,不管系统全局有几个任务栈,而且这个它存在的这个任务栈中只能存在它一个Activity。
如拨号界面,当在TaskB中的ActivityB@1需要调用拨号界面,首先会创建一个TaskA,然后在TaskA中创建拨号界面的唯一实例,再转到TaskB中的拨号界面,然后点击返回,又会跳转回TaskB。