一、概述
在实际开发中,应该为每个Activity指定恰当的启动模式,系统中使用任务栈来存储创建的Activity实例,任务栈是一种“先进后出”的栈结构。比如,我们多次启动同一个Activity,系统会将创建的实例一次加入到任务栈中,当按back返回键时,每按一次,一个Activity出栈,直到栈空为止,然后系统回收空的任务栈。
如上面的Activity没有设置启动模式,你会发现多次启动一个Activity会创建多个Activity实例,浪费内存,那么怎么解决呢?Android的Activity有四种启动模式,根据不同的使用场景选择不同的启动模式,最大化降低每次都要在任务栈中创建一个新实例的压力,降低内存消耗。我们用图片来概括一下这四种启动模式,下面再一一详解。
任务栈先进后出图解:
二、四种模式
1.standard 标准模式
standard是Activity默认的启动模式,如果没有指定启动模式那么所有的Activity都是默认使用standard 标准模式。每次启动Activity都会创建一个新的实例,无论这个实例是否存在于任务栈中。
在standard模式下,每当启动一个新的Activity它就会进入任务栈中,并处于栈顶的位置,对于使用standard模式下的Activity,系统不会判断该Activity在栈中是否存在,每次启动都会创建一个新的实例。
比如,如果这时任务栈中有A、B、C三个Activity,启动模式都为standard,C位于栈顶(如下图一),在A、B、C三个Activity的onCreate()和onDestroy()方法中都打印了log,这时再启动Activity C则会重新创建一个实例,加入任务栈中,并且位于栈顶,打印log如下:
standard 任务栈图解:
图一
使用场景:一般Activity没有特殊要求都是这个模式。
2.singleTop 栈顶复用模式
singleTop模式与standard类似,不同的是如果启动的Activity已经位于栈顶,那么直接复用栈顶的Activity,不需要重新创建,否则都会在栈顶创建新的实例。
详细来说这里分三种情况,第一种:如果任务栈内没有需要启动的Activity实例,那么和standard一样,在栈顶创建Activity实例;第二种:如果任务栈内存在需要启动的Activity实例,并且该实例位于栈顶,那么直接复用该Activity(如下图二左);第三种:如果任务栈内存在需要启动的Activity实例,但是不位于栈顶,也需要重新创建Activity实例(如下图二右)。
例子:任务栈中有A、B、C三个Activity,启动模式都为singleTop,C 位于栈顶,在A、B、C三个Activity的onCreate()、onNewIntent()和onDestroy()方法中都打印了log;
(1)如下图二左,这时再去启动一个Activity C,如果C位于栈顶,那么会直接复用原来在栈顶的Activity C实例。只回调onNewIntent()方法,并没有被销毁(onDestroy()),打印log如下:
(2)如下图二右,这时再去启动一个Activity A,虽然任务栈中存在A的实例,但是它不在栈顶,那么还是会在任务栈创建一个新的Activity A位于栈顶,其他的Acitivity并没有销毁,打印log如下:
singleTop 任务栈图解:
图二
使用场景:适合用于接收通知启动的内容显示页面,当收到多条新闻推送时,接收该新闻的Activity设置成singleTop模式,根据传过来的Intent显示不同的新闻信息,不会启动多个Activity。
3.singleTask 栈内复用模式
单实例模式,需要创建的Activity位于任务栈中时,不会重新创建Activity,而是将该Activity上面的Activity移出任务栈,使它成为栈顶。
如果Activity的启动模式指定为singleTask,那么每次启动Activity时,系统会先检查任务栈中是否存在该Activity的实例,如果存在则直接复用该Activity实例,并且将该Activity上面的所有Activity移出任务栈,使它成为栈顶;如果位于栈顶的,则直接复用该Activity(如下图三左);如果任务栈中没有该Activity实例则直接创建一个新的Activity实例位于栈顶(如下图三右)。
例子:任务栈中有A、B、C三个Activity,启动模式都为singleTask,C 位于栈顶,在A、B、C三个Activity的onCreate()、onNewIntent()和onDestroy()方法中都打印了log;
(1)如下图三左,如果再启动已经存在的Activity C并且实例在栈顶,会直接复用原来在栈顶的Activity C实例,回调onNewIntent()方法,其他的Activity没有变化,打印log如下:
(2)如下图三右,如果再启动已经存在任务栈的Activity A但是该实例不在栈顶,那么该实例A上面的ActivityC、B会被移出任务栈(被销毁),使Activity A位于栈顶并且回调onNewIntent()方法,打印log如下:
singleTask 任务栈图解:
图三
使用场景:这种适用于应用只有一个实例,程序的入口点,比如应用首页,登录界面等。
4.singleInstance 单实例模式
全局单例模式,加强版的singleTask,具有此模式的Activity仅仅能单独位于一个任务栈中。
如果需要整个应用中只有一个实例,可以使用singleInstance模式,与上面三种模式不同的是会启动一个新的任务栈来管理这个Activity,无论是从哪个任务栈中启动该Activity,都只会创建一个Activity实例,并且会使用一个全新的任务栈来装载这个实例。如果启动的Activity不存在,则会先创建一个新的任务栈,再创建该Activity实例;如果启动的Activity已经存在,那么无论位于哪一个任务栈中,都会把该Activity的任务栈调到前台,重用这个实例。
例如:启动一个A Activity,系统新开辟一个任务栈装载这个实例。
图四
使用场景:这个常用于系统的应用,比如Lauch、锁屏键的应用等等,整个系统中仅仅只有一个,我们一般不会用到,了解就可以了。
三、使用方式
启动模式有静态设置和动态设置两种方式,但是两者是有差别的,一般使用静态设置的比较多,使用范围问题,静态设置无法为Activity指定FLAG_ACTIVITY_CLEAR_TOP模式,动态设置则无法为Activity指定singleInstance模式等。
1.静态XML设置
在安卓清单文件AndroidManifest.xml在Activity的跟标签设置启动模式,添加launchMode标记
<activity
android:name=".A_Activity"
android:launchMode="standard" /><!--标准模式-->
<activity
android:name=".B_Activity"
android:launchMode="singleTop" /><!--栈顶复用模式-->
<activity
android:name=".C_Activity"
android:launchMode="singleTask" /><!--栈内复用模式-->
<activity
android:name=".D_Activity"
android:launchMode="singleInstance" /><!--单例实例模式-->
2.动态设置
部分Activity特殊场景需要动态设置启动模式,通过代码Intent.addFlags()动态设置:
Intent intent = new Intent(A_Activity.this, B_Activity.class);
//指定B_Activity为singleTop 栈顶复用模式
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
3.onNewIntent()
注意:当一个Activity设置了singleTop或者singleTask模式以后,那么生命周期则会发生改变,onCreate()方法则不会再次运行,我们一般在onCreate()方法中获取数据并做相关操作,onCreate()都是在Activity创建的时候回调了,跳转的Activity出现复用原Activity的情况,页面的数据一般是通过getIntent()来获取。那么getIntent()一直获取的是旧的数据,就无法获取最新的数据了吗?我们可以通过复写Activity的onNewIntent()方法获取最新数据。
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);//设置新的Intent,否则后面获取的Intent都是旧数据
}
这里的Intent是最新的,需要重新setIntent(intent),设置新的Intent,否则后面获取的Intent都是旧数据。
4.Activity常用的Flags
Activity常用的Flags比较多,这里暂例几种常用的,其他的需要可以参考官方文档
- FLAG_ACTIVITY_NEW_TASK:对应singleTask 栈内复用模式。
- FLAG_ACTIVITY_CLEAR_TOP:如果设置,并且Activity在任务栈中,把该Activity上方的Activity都会关闭,并且这个Intent会作为新的Intent投递到老的Activity中去。一般与FLAG_ACTIVITY_NEW_TASK结合使用。
- FLAG_ACTIVITY_RESET_TASK_IF_NEEDED:如果设置,并且该活动要么在新任务中启动,要么将现有任务放在顶部,那么它将作为任务的前门启动。这将导致应用将任务置于适当状态所需的任何关联(将活动移至或移出任务),或者在需要时将任务重置为初始状态。
- FLAG_ACTIVITY_SINGLE_TOP:对应singleTop 栈顶复用模式。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记位的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到Activity时,此标记位便体现了它的效果。
我们来总结一下这四种模式:
模式 | 说明 | 使用场景 | |
standard | 标准模式 | 默认模式,每次启动Activit都会创建一个新的实例,无论这个实例是否存在于任务栈中。 | 一般Activity没有特殊要求都是这个模式 |
singleTop | 栈顶复用模式 | 如果实例已经位于栈顶,那么直接复用栈顶的实例,不需要重新创建,否则都会在栈顶创建新的实例。 | 适合用于接收通知启动的内容显示页面,当收到多条新闻推送时,根据传过来的Intent显示不同的新闻信息,不会启动多个Activity |
singleTask | 栈内复用模式 | 单实例模式,创建的实例位于任务栈中,不会重新创建,而是将它上面的实例移出任务栈,使它成为栈顶。 | 这种适用于应用只有一个实例,程序的入口点,比如应用首页,登录界面等。 |
singleInstance | 单实例模式 | 全局单例模式,仅仅能单独位于一个任务栈中,会使用一个全新的任务栈来装载实例。 | 这个常用于系统的应用,比如Lauch、锁屏键的应用等等,整个系统中仅仅只有一个,一般不会用到,了解即可。 |