2.4 Activity启动模式

通过2.3节的学习,读者应该明白了这样一个事实,一个应用程序当中通常都会包含很多个Activity,多个Activity之间还应该是可以相互启动的。在Activity启动时是有不同模式的,本节将重点讲解Activity的4种启动模式。

启动模式在多个Activity跳转的过程中扮演着重要的角色,可以决定是否生成新的Activity实例、是否重用已存在的Activity实例、是否和其他Activity实例共用一个Task。Task是与Activity相关的一个重要概念,密切联系着Activity栈,是一组以栈的模式聚集在一起的Activity组件集合,Task以栈来管理Activity。一个Task可以管理多个Activity,启动一个应用,也就创建了一个与之对应的Task。简单地说就是Activity是Task用栈的方式进行管理的。

在Android中Activity包括standard、singleTop、singleTask以及singleInstance四种启动模式。我们可以在AndroidManifest.xml中配置<activity>的android:launchMode属性为以上任一种模式。下面我们结合实例一一介绍这4种启动模式。

2.4.1 standard模式

standard是默认的启动模式,如果不指定launchMode属性,就会自动使用这种启动模式。创建一个Android工程,新建一个MainActivity类来具体分析,代码如下:

布局文件代码如下:

本例中使用了button按钮和textView文本框,读者知道即可,下面章节会有具体讲解。这里主要看代码中的关键部分,在MainActivity中使用Intent显式启动了MainActivity,并显示当前Activity对象的hash值。运行程序,进入的Activity界面如图2-7所示。

点击MAINACTIVITY按钮,进入的界面如图2-8所示。

图2-7 原始的MainActivity界面

图2-8 跳转之后的MainActivity界面

图2-9 继续跳转之后的MainActivity界面

接着点击MAINACTIVITY按钮,进入的界面如图2-9所示。

不管点击多少次,虽然显示的都是MainActivity对象,但是都是不同对象。这种启动模式表示每次启动该Activity时都会创建一个新的实例,并且总会把它放入当前的任务当中。声明为这种启动模式的Activity可以被实例化多次,一个任务当中也可以包含多个Activity的实例。

2.4.2 singleTop模式

直接使用上面的例子,并在AndroidManifest.xml中配置<activity>的android:launchMode属性为singleTop,代码如下:

运行程序,会进入如图2-10所示的界面。

图2-10 当MainActivity处于栈顶时界面的状态

此时,不管点击多少次按钮,打印的MainActivity对象都是同一个。此时,有的读者可能会说使用singleTop模式启动的Activity不会创建多个实例,使用的都是同一个Activity实例。其实这是不对的。下面我们再来看另一个例子。在这个例子中,我们再创建一个OtherActivity,代码如下:

布局文件和MainActivity的布局文件基本一致,只需要将textView和Button对应的Id修改为和上述代码中的Id一致即可。

图2-11 MainActivity不处于栈顶时的状态

另外,需要将MainActivity中的Intent部分代码修改为:

运行程序,点击按钮从MainActivity先跳转到OtherActivity,再点击按钮跳转到MainActivity,过程如图2-11所示。

从图2-11中可以清晰地发现,MainActivity创建了不同的实例。那么为什么同样是使用singleTop模式,在上一个例子中MainActivity只创建了一个实例呢?singleTop模式的启动方式到底是什么样的呢?

其实,对于singleTop模式,如果要启动的这个Activity在当前任务中已经存在了,并且还处于栈顶的位置,那么系统就不会再去创建一个该Activity的实例,而是调用栈顶Activity的onNewIntent()方法(读者在练习时可以进行观察,这里不做分析了)。声明成这种启动模式的Activity也可以被实例化多次,一个任务当中也可以包含多个这种Activity的实例。

举个例子来讲,一个Task的返回栈中有A、B、C、D四个Activity,其中A在最底端,D在最顶端。这时如果我们要求再启动一次D,并且D的启动模式是standard,那么系统就会再创建一个D的实例放入返回栈中,此时栈内元素为:A-B-C-D-D。而如果D的启动模式是singleTop,由于D已经是在栈顶了,那么系统就不会再创建一个D的实例,而是直接调用D Activity的onNewIntent()方法,此时栈内元素仍然为:A-B-C-D。

2.4.3 singleTask模式

在上面例子的基础上,将AndroidManifest.xml文件中MainActivity与OtherActivity的launchMode属性修改为android:launchMode="singleTask"。运行程序,多次点击按钮,过程如图2-12所示。

图2-12 singleTask模式下Activity启动状态

显然,声明了使用singleTask模式启动的MainActivity只创建了一个实例,虽然MainActivity并没有位于Task返回栈的栈顶,但是同样使用了singleTask模式启动的OtherActivity却创建了新的实例。

singleTask这种启动模式表示,系统会创建一个新的任务,并将启动的Activity放入这个新任务的栈底位置。但是,如果现有任务当中已经存在一个该Activity的实例了,那么系统就不会再创建一次它的实例,而是会直接调用onNewIntent()方法。声明成这种启动模式的Activity在同一个任务当中只会存在一个实例。

读者会很疑惑,因为按照上述解释,OtherActivity也不应该创建新的实例。这是因为这里我们所说的启动Activity都指的是启动其他应用程序中的Activity,因为singleTask模式在默认情况下只有启动其他程序的Activity才会创建一个新的任务,启动自己程序中的Activity还是会使用相同的任务。如果启动的对象是本应用内的Activity,那么如果发现有对应的Activity实例,就使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前。本例中,OtherActivity启动MainActivity时,发现在Task的返回栈中有对应的MainActivity实例,于是把它上面的其他Activity实例都推出栈,它成为栈顶对象,OtherActivity也因此被推出了栈。所以当第二次启动OtherActivity时,并没有在Task的返回栈中找到对应的实例,只能创建一个新的实例。很多Android书籍中并未指出此种情况,读者需要多加注意。

2.4.4 singleInstance模式

使用singleInstance模式启动Activity会先创建一个新的Task,这种Activity所在的Task中始终只会有一个Activity,通过这个Activity打开的其他Activity会被放入到别的任务当中。

修改MainActivity的launchMode属性为android:launchMode="standard",修改OtherActivity的launchMode属性为android:launchMode="singleInstance"。为了能够展示出使用singleInstance模式启动Activity会先创建一个新的Task这种效果,加入如下代码以展示taskId:

在布局文件中加入如下代码:

这是在MainActivity中的实例,在OtherActivity中修改的内容除了Id以外,和MainActivity中的一样。

运行程序,从MainActivity中跳转到OtherActivity,再到MainActivity,再到OtherActivity,效果如图2-13所示。

图2-13 singleInstance模式下Activity启动状态

可以很清晰地发现,系统确实为使用了singleInstance模式启动的OtherActivity创建了新的Task。而且在这个Task返回栈中的Activity实例是同一个,当它在去启动MainActivity时,MainActivity依旧进入了ID为135的Task中,并没有留在当前的Task中。

按Back键第一次返回时,会进入上一个界面,如图2-14所示。

再按一次返回键后退时并不会返回OtherActivity,而是直接返回到上一个MainActivity,如图2-15所示。

图2-14 第一次按返回键时的界面

图2-15 第二次按返回键时的界面

这里读者可能会提出为什么没有退到第二次的OtherActivity中呢?这是因为,在Id为135的Task中创建了两个MainActivity实例,由于使用了singleInstance模式,因此在Id为136的Task中只有一个OtherActivity实例,所以当在OtherActivity界面按返回键后当前Task的返回栈空了,应用回到了Id为135的Task中,此时第二次出现的MainActivity实例处于栈顶并显示在界面中,再按返回键,最初的MainActivity实例进入栈顶显示在界面中。如果此时再按返回键,当前Task中的返回栈也将为空,退出应用。

使用singleInstance模式的好处就是当多个应用或者Activity启动某个Activity(使用singleInstance模式启动)时,共用同一个返回栈,解决了共享Activity实例的问题。

通过上面的论述,读者对4种启动模式应该有了较深的了解。standard模式是Android默认的启动模式。但是使用standard模式启动Activity可能会造成多次启动的问题,比如用户手误多次点击一个跳转到新的Activity的按钮,这时系统就会创建多个新的Activity,但是用户其实只需要一个。我们借助singleTop模式来避免这个问题。将Activity的启动模式指定为singleTop,在启动Activity时如果发现返回栈的栈顶已经是该Activity,就认为可以直接使用它,不会再创建新的Activity实例。如果该Activity并没有处于栈顶的位置,还是可能会创建多个Activity实例的。将Activity的启动模式指定为singleTask,每次启动该Activity时系统首先会在返回栈中检查是否存在该Activity的实例,如果已经存在就直接使用该实例,并把在这个Activity之上的所有活动统统出栈,如果没有就会创建一个新的Activity实例。不同于以上3种启动模式,指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动,这样做的好处就是解决了多个应用访问一个Activity时的共享实例问题。