什么是任务栈
Android系统中,Activity代表一个个窗口,负责直接和用户进行交互。一个应用包含很多的窗口即Activities,同时可以从这个应用去打开存在于其他应用中的activities例如从Photo应用要分享一张照片到朋友圈,则会通过当前的Photo应用启动微信APP。为了实现多个应用的无缝对接体验,Android系统使用任务栈(back stack)来对Activities进行管理。
一个任务是用户在执行某种工作时所需要的activities的集合。Android系统使用任务栈(back stack)来对Activities进行管理 ,任务栈是一种“后进先出”的栈结构。默认情况下,当我们启动一个Activity时,系统会创建一个Activity实例,并将其入栈;每按下一次back键就会有一个Activity出栈,直到栈空为止,当栈中无任何Activity时,系统会回收这个任务栈。以下展示了activities在stack中的行为变化。
启动模式
我们知道在默认情况下,启动一个Activity,就会重新创建这个Activity并将其压入任务栈栈顶并显示,但这么做会有一个问题:当我们多次启动同一个Activity时,系统会重复创建多个实例,导致系统资源的浪费,显然需要提供一种方法来控制Activity启动时是否要创建新实例,若不需要创建则该从哪个任务栈中获取该Activity实例,这就是启动模式的作用。
启动模式简单地说就是Activity启动时的策略,可以通过设置manifest中activity的lanchMode属性或者通过传到startActivity()的intent的flag来设置。启动模式包括standard、singleTop、singleTask、singleInstance四种:
standard
标准模式,也是系统的默认模式。每次启动Activity时,都会重新创建一个新的实例,并放入任务栈。比如说我们启动一个LauchModeActivity1,指定该Activity的启动模式为standard,然后通过startActivity重新再启动LauchModeActivity1,我们可以通过adb shell dumpsys activity activities 来观察当前的任务栈状态:
我们可以看出,前台任务栈的taskAffinity值为com.example.myandroiddemo, taskId为84,当前栈中共有四个Activity,Run#0为栈底、Run#3为栈 顶,在stardard模式下,每次启动LauchModeActivity1时,都会重新创建一个新的实例,并放入当前任务栈。
singleTop
栈顶复用模式。当启动一个Activity时,此Activity默认会进入启动它的Activity所属的任务栈中,在创建之前,会先判断当前任务栈顶是否存在该Activity实例。若存在,那么此Activity不会被重新创建,同时它的onNewIntent方法会被调用;若不存在,则重新创建此Activity并压入栈顶。比如说还是LauchModeActivity1启动自身的例子,当前的任务栈状态为:
singleTask
栈内复用模式。这是一种单实例模式,在这种模式下,只要Activity在任务栈(多个)中存在,就不会重新创建实例,系统会回调其onNewIntent。当启动一个app时,一般情况下都会新建一个任务栈,每个任务栈都会有一个标识taskAffinity,默认taskAffinity为当前应用的包名,我们可以在Activity里单独指定taskAffinity属性。当启动一个singleTask的Activity时,首先会先根据taskAffinity 查找是否存在当前Activity所需的任务栈,若不存在则新建一个任务栈;然后在全部的任务栈内查找是否存在此Activity的实例,若不存在就新建一个压入相关的任务栈,若存在则先判断找到的Activity所在的任务栈和所需的任务栈是否是同一个:若是同一个就直接将找到的Activity之上的activities弹出即可,若不是则除了弹出在其之上的activities,还需将此Activity压入所需的任务栈中。比如说,通过LauchModeActivity1启动LauchModeActivity2,然后再通过LauchModeActivity2启动LauchModeActivity1,当LauchModeActivity1设置为singleTop时的任务栈状态为:
可以看到一共产生了4个Activity实例,同属于任务栈com.example.myandroiddemo。
当LauchModeActivity1设置为singleTask 时Activity的生命周期及相关的任务栈状态为:
可以看到当通过LauchModeActivity2重新启动LauchModeActivity1时,LauchModeActivity2被销毁,LauchModeActivity1重新出现在栈顶中,并调用了onNewIntent方法,该任务栈始终只会存在LauchModeActivity1 f65b163这个实例,直至当前栈被清空。
接下来,我们来看一下taskAffinity的使用:通过LauchModeActivity1启动LauchModeActivity2,然后再通过LauchModeActivity2启动LauchModeActivity3,最后再通过LauchModeActivity3重新启动LauchModeActivity1,设置LauchModeActivity1、LauchModeActivity3 为singleTask,并为LauchModeActivity3设置taskAffinity属性:
<activity
android :name= ".activity.LauchModeActivity1"
android :launchMode= "singleTask" />
<activity
android :name= ".activity.LauchModeActivity2" />
<activity
android :name= ".activity.LauchModeActivity3"
android :taskAffinity= "com.example.singletask"
android :launchMode= "singleTask"/>
则其中Activity的生命周期切换及相关任务栈状态为:
当从LauchModeActivity3重新启动LauchModeActivity1时,所需任务栈为92,LauchModeActivity1位于任务栈91,所以首先弹出LauchModeActivity1之上的LauchModeActivity2,然后将LauchModeActivity1实例移动至LauchModeActivity3所在的任务栈。
singleInstance
单实例模式。一种加强的singleTask模式,除了具有singTask模式的特性,还包括具有此种模式的Activity只能单独位于一个任务栈中。比如说还是上述例子,更改Activity的属性为:
<activity
android :name= ".activity.LauchModeActivity1"
android :launchMode= "singleTask" />
<activity
android :name= ".activity.LauchModeActivity2"
android :launchMode= "singleInstance"/>
<activity
android :name= ".activity.LauchModeActivity3" />
则其中Activity的生命周期切换及相关任务栈状态为:
附录
相关代码:本篇例子可在MyAndroidDemo中的Activity LauchMode中找到
参考资料:
- google 官方API
- android 开发艺术探索
- Android入门:Activity四种启动模式
- Tasks and Back stack 详解