Activity 的 LaunchMode
默认情况下,多次启动同一个 Activity 时,系统会创建多个实例并把它们一一放入任务栈中,当我们按 back 健时,这些 Activity 会一一回退。任务栈是一种“后进先出”的栈结构,即每按一下 back 健就会有一个 Activity 出栈,直到栈空为止,当栈中无任何 Activity 时,系统会回收这个任务栈。
四种启动模式:standard、singleTop、singleTask 和 singleInstance。下面介绍各种启动模式的含义及使用例子。
一、standard – 默认模式
standard:标准模式也是系统的默认模式。每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在。这种模式下,谁启动了这个 Activity,那么这个 Activity 就运行在启动它的那个 Activity 所在的栈中。如 Activity A 启动了 Activity B(B 是标准模式),那么 B 就会进入到 A 所在的栈中。
当我们用 ApplicationContext 去启动 standard 模式的 Activity 时会报错,错误如下:
android.util.AndroidRuntimeException: Calling startActivity() from outside
of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.
Is this really what you want?
这是因为 standard 模式的 Activity 默认会进入启动它的 Activity 所属的任务栈中,由于非 Activity 类型的 Context (如 ApplicationContext)并没有所属的任务栈,所以就出问题了。解决方法是为待启动 Activity 指定 FLAG_ACTIVITY_NEW_TASK 标记位,这样启动的时候会创建一个新的任务栈,这时待启动的 Activity 是以 singleTask 模式启动的
二、singleTop – 栈顶复用模式
singleTop 栈顶复用模式。如果新 Activity 已经位于任务栈的栈顶,那么此 Activity 不会被重新创建,同时它的 onNewIntent 方法会被调用,通过此方法的参数可以取出当前请求的信息。需要注意的是,这个 Activity 的 onCreate、onStart 不会被系统重新调用,因为它并没有发生改变。如果新 Activity 的实例已经存在但不是位于栈顶,那么新 Activity 仍然会重建。
适合接收通知启动的内容显示页面,当收到多条新闻推送时,用于展示新闻的 Activity 设置成此模式,根据传来的 Intent 数据显示不同的新闻信息,不会启动多个 Activity。
三、singleTask – 栈内复用模式
singleTask:栈内复用模式。这是一种单实例模式,在这种模式下,只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例,复用时会将它上面的 Activity 全部出栈,同时它的 onNewIntent 方法会被调用。这个过程存在一个任务栈匹配,因为这个模式启动时会在自己需要的任务栈中寻找实例,这个任务栈通过 taskAffinity 属性指定,如果这个任务栈不存在,则会创建这个任务栈。
taskAffinity 标识了一个 Activity 所需的任务栈的名字,默认情况下,所有 Activity 所需的任务栈的名字为应用的包名。我们可以为每个 Activity 都单独指定 TaskAffinity 属性,这个属性必须不能和包名相同,否则就相当于没有指定。TaskAffinity 属性主要和 singleTask 启动模式或者 allowTaskReparenting 属性配对使用。另外,任务栈分为前台任务栈和后台任务栈,后台任务栈中的 Activity 处于暂停状态,用户可以通过切换将后台任务栈再次调到前台。
适合作为程序入口点,例如浏览器的主界面,不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走 onNewIntent,并且会清空主界面上的其它页面
四、singleInstance – 单实例模式
singleInstance:单实例模式。该模式除了具备 singleTask 模式的所有特性外,该模式的 Activity 只能单独的位于一个任务栈中,具有全局唯一性,即整个系统中只有这一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例
如何指定 Activity 的启动模式?
(1)通过 AndroidMenifest 为 Activity 指定启动模式,如下所示:
android:name=".activity.protocol.ProtocolActivity"
android:launchMode="singleTask"/>
(2)通过 Intent 中设置标志位来为 Activity 指定启动模式,如下所示:
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
优先级上第二种优先级高于第一种,当两种同时存在时,以第二种方式为准;这两种方式的限定方式不同,第一种无法直接为 Activity 设定 FLAG_ACTIVITY_CLEAR_TOP 标识,第二种无法为 Activity 指定 singleInstance 模式。
Activity 常用 Flags
FLAG_ACTIVITY_NEW_TASK
为 Activity 指定 singleTask 启动模式,效果和在 XML 中指定该模式相同
FLAG_ACTIVITY_SINGLE_TOP
为 Activity 指定 singleTop 启动模式,效果和在 XML 中指定该模式相同
FLAG_ACTIVITY_CLEAR_TOP
具有此标记的 Activity,当它启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标记的 Activity 不会出现在历史 Activity 的列表中,在某些情况下我们不希望用户通过历史列表回到我们的 Activity 的时候这个标记比较有用。它等同于在 XML 中指定 Activity 的属性 android:excludeFromRecents="true"。