Activity四种启动模式
这部分应该是最最基础的了,但是还是有很多细节需要把握,不只是表面的知识点。
- 1 Activity的管理是采用任务栈的形式
- 2 任务栈采用“后进先出”的栈结构
- 3 每按一次Back键,就有一个Activity出栈
- 标准模式(standard)
每启动一次Activity,就会创建一个新的Activity实例并置于栈顶谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。也就是说在ActivityA中启动了ActivityB那么ActivityB就在ActivityA的栈中
- 单例模式(singleInstance)
- 作为栈内复用模式(singleTask)的加强版
- 打开该Activity时,直接创建一个新的任务栈,并创建该Activity实例放入新栈中
- 一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例
- 使用场景:多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。使用场景如闹铃提醒,将闹铃提醒与闹铃设置分离。
四种启动模式图解
四种启动模式的区别
- 决定打开的任务栈
standard、singleTop启动模式的Activity的目标任务栈,和收到Intent的发送者在同一个任务栈内。
singleTask启动模式打开的任务栈由参数TaskAffinity决定。
singleInstance启动模式总是新建任务栈,不会被启动到一个其他任务栈里。 - 是否允许多个相同的Activity实例
standard、singleTop启动模式中,同一个Activity可以被实例化多次,并且存在于不同的任务栈中,且一个任务栈可以包括同一个Activity的多个实例;
singleTask、singleInstance启动模式则限制只生成一个实例。 - 是否允许不同的Activity实例存在于同一个任务栈内
singleInstance启动模式独占一个任务栈,其它Activity实例不能存在于该任务栈里。
另外三种模式,则可以和其它Activity实例共存于一个任务栈。 - 是否每次都生成新实例
standard模式:每次都生成新实例。
singleTop模式:若启动的Activity不在栈顶,则生成新实例;
singleInstance模式:所在栈的唯一Activity实例,只会实例化一次,以后每次都被重用。
singleTask模式:若启动的Activity不在栈内,则生成新实例;
启动模式的设置
启动模式有两种设置方式: - 在AndroidMainifest设置
- 通过Intent设置标志位
1.在AndroidMainifest中设置
2.通过Intent设置标志位
二者设置的区别
- Intent设置方式比Manifest设置方式的优先级要高,即以前者为准
- 限定范围不同
Manifest设置方式无法设定FLAG_ACTIVITY_CLEAR_TOP标识;Intent设置方式无法设置单例模式(singleInstance)
介绍一下任务栈:
(1)程序打开时就创建了一个任务栈, 用于存储当前程序的activity,所有的activity属于一个任务栈。
(2)一个任务栈包含了一个activity的集合,去有序的选择哪一个activity和用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。
(3)任务栈可以移动到后台,并且保留了每一个activity的状态. 并且有序的给用户列出它们的任务,而且还不丢失它们状态信息。
(4)退出应用程序时:当把所有的任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。
任务栈的缺点:
(1)每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用户体验差,需要点击多次返回才可以把程序退出了。
(2)每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余,重复数据太多,会导致内存溢出的问题(OOM)。
为了解决任务栈的缺点,我们引入了启动模式。
Standard
默认模式 v,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。
若我有一个Activity名为A1, 上面有一个按钮可跳转到A1。那么如果我点击按钮,便会新启一个Activity A1叠在刚才的A1之上,再点击,又会再新启一个在它之上…….
点back键会依照栈顺序依次退出。
singleTop
可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。
例如:
若我有两个Activity名为B1,B2,两个Activity内容功能完全相同,都有两个按钮可以跳到B1或者B2,唯一不同的是B1为standard,B2为singleTop。
若我意图打开的顺序为B1->B2->B2,则实际打开的顺序为B1->B2(后一次意图打开B2,实际只调用了前一个的onNewIntent方法)
若我意图打开的顺序为B1->B2->B1->B2,则实际打开的顺序与意图的一致,为B1->B2->B1->B2。
singleTask
只有一个实例。在同一个应用程序中启动的它时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task中共存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。
例如:
若我的应用程序中有三个Activity,C1,C2,C3,三个Activity可互相启动,其中C2为singleTask模式,那么,无论我在这个程序中如何点击启动,如:C1->C2->C3->C2->C3->C1-C2,C1,C3可能存在多个实例,但是C2只会存在一个,并且这三个Activity都在同一个task里面。但是C1->C2->C3->C2->C3->C1-C2,这样的操作过程实际应该是如下这样因为singleTask会把task中在其之上的其它Activity destory掉。
操作:C1->C2 C1->C2->C3 C1->C2->C3->C2 C1->C2->C3->C2->C3->C1 C1->C2->C3->C2->C3->C1-C2
实际:C1->C2 C1->C2->C3 C1->C2 C1->C2->C3->C1 C1->C2
若是别的应用程序打开C2,则会新启一个task。
如别的应用Other中有一个activity,taskId为200,从它打开C2,则C2的taskIdI不会为200,例如C2的taskId为201,那么再从C2打开C1、C3,则C1、C3的taskId仍为201。
注意:如果此时你点击home,然后再打开Other,发现这时显示的肯定会是Other应用中的内容,而不会是我们应用中的C1 C2 C3中的其中一个。
singleInstance
只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。
例如:
加载该Activity时如果没有实例化,他会创建新的Task后,实例化入栈,如果已经存在,直接调用 onNewIntent,该Activity的Task中不允许启动其它的Activity,任何从该Activity启动的其他Activity都将被放到其他task中,先检查是否有本应用的task,没有的话就创建。
程序有三个ActivityD1,D2,D3,三个Activity可互相启动,其中D2为singleInstance模式。那么程序从D1开始运行,假设D1的taskId为200,那么从D1启动D2时,D2会新启动一个task,即D2与D1不在一个task中运行。假设D2的taskId为201,再从D2启动D3时,D3的taskId为200,也就是说它被压到了D1启动的任务栈中。
singleInstance附加解释:
这种启动模式比较特殊,因为它会启用一个新的栈结构,将Activity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。
我们修改MainActivity的launchMode=”standard”,SecondActivity的launchMode=”singleInstance”,由于涉及到了多个栈结构,我们需要在每个Activity中显示当前栈结构的id,所以我们为每个Activity添加如下代码:
TextView textview=(TextView)findViewById(R.id.tv); textview.setText("current tesk id"+this.getTaskId());
我们发现这两个Activity实例分别被放置在不同的栈结构中,关于singleInstance的原理图如下
上半部分图我们看到从MainActivity跳转到SecondActivity时,重新启用了一个新的栈结构,来放置SecondActivity实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在SecondActivity中再次跳转到MainActivity,这个时候系统会在原始栈结构中生成一个MainActivity实例,然后回退两次,注意,并没有退出,而是回到了SecondActivity,为什么呢?是因为从SecondActivity跳转到MainActivity的时候,我们的起点变成了SecondActivity实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构。