前言

本篇博客主要介绍Activity的几种启动模式的区别,也许有人会不屑一顾,觉得这是基础中的基础,不就那么四种启动模式么?不过这四种启动模式中的弯弯道道还是有很多的,只知道四种启动模式是不够的,还需要知道其中的细节。

在理解四种启动模式之前你需要知道的东西

Q:如何设置Activity的启动模式

A:通过配置AndroidManifest.xml中<activity android:launchMode=”” >标签,还可以通过在启动Activity的Intent中通过intent.addFlags(flags)添加启动flags(这个在最后面讨论)

Q:什么是Activity任务栈

A:Activity任务栈可以看做与用户交互的一系列 Activity。 这些 Activity 按照打开的先后顺序排列在一个栈结构中,用户按下返回键一个个回退,直到返回到桌面,任务栈中可以放置非自身的Activity,比如在自己的应用中打开浏览器,若无特殊配置,那么浏览器所在的Activity会放置在本应用的任务栈中。

Q:怎么判断当前任务栈

A:在Activity中通过getTaskId()即可获取到任务栈的id,这个id是递增的。

Q:系统怎么知道哪些Activity放置在一个任务栈中,如果我打开一个应用,然后从桌面打开另一个应用,是一个任务栈么?

A:后面一个问题的答案是:不是同一个任务栈,一般来说,如果没有特殊的设置启动模式,一个应用只有一个任务栈,“栈名”默认为包名,可以通过<activity android:taskAffinity=”” >属性进行设置,当应用启动以后,系统会创建一个”栈名”为包名的任务栈,以后本应用的Activity以及被本应用启动的其他Activity均会被放置在这个任务栈中,注意:<activity android:taskAffinity=”” >如果要生效,需要将Activity的启动模式设置为singleTask或者singleInstance。或者在启动Activity的Intent中加入FLAG_ACTIVITY_NEW_TASK标记即可

四种启动模式

首先贴出官方文档上面关于四种启动模式的介绍,这也是大多数人知道的部分,就不详细介绍了。

用例 启动模式 多个实例 注释
大多数 Activity 的正常启动 standard 默认值。系统始终会在目标任务中创建新的 Activity 实例并向其传送 Intent。
singleTop 有条件 如果目标任务的顶部已存在一个 Activity 实例,则系统会通过调用该实例的onNewIntent() 方法向其传送 Intent,而不是创建新的 Activity 实例。
专用启动(不建议用作常规用途) singleTask 系统在新任务的根位置创建 Activity 并向其传送 Intent。 不过,如果已存在一个 Activity 实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建新的 Activity 实例。
singleInstance 与singleTask相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。 该 Activity 始终是其任务唯一仅有的成员。

表格来自:Google文档

上表中模式分为两大类,“standard”和“singleTop”Activity 为一类,“singleTask”和“singleInstance”为另一类。使用“standard”或“singleTop”启动模式的 Activity 可多次实例化。 实例可归属任何任务栈,并且可以位于 Activity 堆栈中的任何位置。 它们通常启动到名为 startActivity() 的任务之中(除非 Intent 对象包含 FLAG_ACTIVITY_NEW_TASK 指令,在此情况下会根据taskAffinity属性选择其他任务)。相比之下,“singleTask”和“singleInstance”Activity 只能启动任务。 他们始终位于任务栈的栈顶,如果栈中已经存在实例,那么会将此实例上面的所有Activity出栈。

个人理解时间

好了,上面copy了一大堆官方文档,估计如果有人看到这里就晕晕乎乎了,下面就说说个人通过写Demo得出的个人理解。

standard模式

这种模式为Activity的默认启动模式,在这种模式下,启动一次Activity,那么就会创建一个Activity实例,并放到当前的任务栈中。效果如下,多次启动,生成了多个实例。

singleTop模式

这种模式叫栈顶复用模式,如果当前要启动的Activity在栈顶,那么此Activity不会继续启动,而是调用此Activity的onNewIntent方法,如下图

如果要启动的Activity不在栈顶,那么则和standard一样,创建一个新的实例。如下图

singleTask模式

这种模式叫栈内复用模式。是指任务栈中只能存在一个此Activity,如果此Activity已经存在,那么会调用其onNewIntent方法,同时如果此Activity不在栈顶,那么将会将它前面的所有Activity出栈。

如下所示,id为9的栈中有一个MainActivity的启动模式为singleTask,然后启动一个MainActivity2,让MainActivity不在栈顶,这时在MainActivity2中启动MainActivity,不会直接创建MainActivity,而是调用了MainActivity的onNewIntent,并将MainActivity2出栈。

那么问题来了,栈内复用,到底是怎么选择哪一个栈呢?

在最上面的问题中其实已经提到了,使用singleTask模式启动Activity,首先会去找”栈名”相同的栈,然后进行栈内复用,如果找不到那就根据<activity android:taskAffinity=”” >属性创建一个新任务栈,并将自己放入。

回到上图中来,MainActivity的启动模式为singleTask,但是我没有设置android:taskAffinity属性,那么默认为应用包名,当在MainActivity2中启动MainActivity的时候,会先去寻找相同”栈名”的任务栈,很明显,MainActivity与MainActivity2在同一个任务栈中,所以直接进行栈内复用,将MainActivity2出栈了。

下面来验证下找不到”同名”任务栈的情况:

如下图,将MainActivity2设置为singleTask启动模式,并且设置MainActivity2的android:taskAffinity属性为”com.other”,与包名不同,当启动MainActivity2的时候,创建了一个新的任务栈用来放置MainActivity2。

通过MainActivity2启动的Activity也会放置在自己的栈中。

简单来说:singleTask指定了单独的任务栈则在单独的任务栈中启动,如果没有单独设置任务栈,则在当前任务栈显示,并且如果不在当前栈顶的话,会销毁此acivity所在栈之前的全部activity并让自己在栈顶显示。

singleInstance模式

singleInstance模式可以看做加强版的singleTask模式,不同之处在,他只允许任务栈中存在他自己。并且不设置android:taskAffinity属性,也会新创建一个任务栈。

如下图所示,MainActivity启动了MainActivity2,处理过程同singleTask模式,这时在MainActivity2中启动MainActivity,MainActivity不在MainActivity2所在的栈里,保证了MainActivity2所在的任务栈只有它自己。

通过FLAG_ACTIVITY_*标记去启动Activity

上面我介绍的四种启动模式,都是在AndroidManifest.xml中直接进行配置的,但是这样很不灵活,下面就来介绍下通过intent.addFlags(flags)配置的方法来动态设置启动模式。

根据官方文档,一共可以使用FLAG_ACTIVITY_NEW_TASK、FLAG_ACTIVITY_CLEAR_TOP、FLAG_ACTIVITY_SINGLE_TOP三种FLAG。

FLAG_ACTIVITY_NEW_TASK

在新的任务栈中启动Activity。

FLAG_ACTIVITY_SINGLE_TOP

如果待启动的Activity在栈顶,那么久不创建新的实例,而是调用其onNewIntent方法,如果不在栈顶,那么就创建一个实例。

FLAG_ACTIVITY_CLEAR_TOP

使用此标识,会造成所有Activity全部出栈,并且创建一个新的实例放在栈中。不管待Activity是否在栈顶。

参考链接:

https://developer.android.com/guide/components/tasks-and-back-stack.html

https://developer.android.com/guide/topics/manifest/activity-element.html

最后修改日期: 2017年9月8日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。