之前的一篇文章中详细说明了Activity的生命周期,说明了Activity中的回调方法是如何被触发的。在用户使用App时,每一个 Activity 都处于某一个状态,对于开发者来说,是无法控制其应用程序处于某一个状态的,这些均由系统来完成。那么Activity的状态又按照哪种逻辑来运行的呢?这就要了解一下Activity栈。
Activity栈
每一个Activity的状态可以说都是由它所在的Activity栈中的位置所决定的。当一个新的Activity启动时,当前的正在和用户进行交互的Activity将会移动到栈顶。如果用户按后退键或者结束前台的Activity时,该Activity将会被移出栈而消亡,而此时栈顶的Activity将变为活动状态。如下图:
。
Activity的状态
一般来说Activity可以分为4种状态:
- 运行:当一个Activity在栈顶时,它是有焦点的,可与用户交互的。系统会尽可能的保持它的活动状态,甚至销毁其他Activity来确保当前的Activity有足够的资源可用。当另外一个Activity被激活的时候,这个Activity会被暂停。
- 暂停:当一个Activity可视但是没有焦点时,那么该Activity就进入了暂停状态。此时该Activity仍然是可运行状态,只不过不可以与用户交互。但是在一些特殊情况下,如内存不足时,系统会选择销毁一个或多个暂停状态的Activity来为当前运行的Activity提供足够的资源。当一个Activity被完全隐藏,它将编程停止状态。
- 停止:当一个Activity是不可见的,它就进入了停止状态,但是这个Activity仍在内存中保有它大的所有状态。但是当其他的地方需要内存时,它将是最有可能被销毁的。一旦一个Activity被关闭或者退出,那么它将成为待用状态。
- 待用:在一个Activity被销毁到被装载前,它都是处于待用状态的。待用状态的Activity不再Activity栈中。
下面我们创建三个Activity,FirstActivity,SecondActivity,ThirdActivity。这三个Activity中都有一个按钮,点击FirstActivity中的按钮可以跳转到SecondActivity;SecondActivity中的可以跳转到ThirdActivity;ThirdActivity中的可以跳转到拨打电话界面 。这样我们的程序事实上可以创建四个Activity。具体实例详见ActivityStackDemo。
我们在这三个Activity中都实现了onCreate,onStop,onPause,onRestart,onDestroy方法,每个方法都有一条输出语句用来表示当前Activity所处的状态。
我们运行程序,进入FirstActivity,查看控制台,FirstActivity的onCreate正常执行。点击按钮,跳转到SecondActivity,发现FirstActivity的onPause,onStop方法正常执行,SecondActivity也执行了onCreate方法。点击按钮跳转到ThirdActivity时,发现SecondActivity执行了onDestroy方法。因为我们在SecondActivity的startActivity方法后执行了Finish方法,从而结束了SecondActivity。仙子按返回键,程序直接跳转到了FirstActivity中。说明SecondActivity已经从Activity栈中移除。
ActivityStackDemo详解:
- 应用程序启动之后,运行的第一个 Activity ( FirstActivity ),该 FirstActivity 对象被压入到 Stack 当中。
当点击了 FirstActivity 的按钮之后,启动第二个 Activity ( SecondActivity ),该 SecondActivity 对象被压入到 Stack 当中
注:现在 FirstActivity 处在 Stack 的底部, ThirdActivity 处于 Stack 的顶部,手机永远显示的都是 Stack 顶部的元素,所以现在界面显示的是 ThirdActivity 。
最后点击 ThirdActivity 的按钮之后,启动 Android 自带的通信的应用程序,该PhoneCall Activity 对象被压入到 Stack 当中。
注:同理,现在 FirstActivity 仍然处在 Stack 的底部, PhoneCall Activity 对象处于 Stack 的顶部,所以现在界面显示的是 PhoneCall Activity 对象的界面。
(以上执行的是压栈操作,现在我们点击返回来看弹栈操作)点击模拟器右侧的 Back 按钮之后,这个时候 PhoneCall Activity 对象被从栈中弹出来。 PhoneCall Activity 对象被弹出来之后, ThirdActivity 又变成栈的顶部,当前程序显示的就是 ThirdActivity 的内容。(因为栈遵循后进先出的原则)
再点击 Back 按钮, ThirdActivity 被从栈中弹出来,程序显示 FirstActivity 的内容。
这里笔者特别提醒一下:在整个栈当中, Activity 只有弹出和压入这两个动作,是不允许调换 Activity 之间的顺序的。
Activity的加载模式
在Android的多Activity开发中,Activity之间的跳转可能需要有多种方式,有时是普通的生成一个新实例,有时希望跳转到原来某个Activity实例,而不是生成大量的重复的Activity。加载模式便是决定以哪种方式启动一个跳转到原来某个Activity实例。
在Android里,有4种Activity的启动模式,分别为:
- standard: 标准模式,一调用startActivity()方法就会产生一个新的实例。
- singleTop: 如果已经有一个实例位于Activity栈的顶部时,就不产生新的实例,而只是调用Activity中的newInstance()方法。如果不位于栈顶,会产生一个新的实例。
- singleTask: 会在一个新的task中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了。
singleInstance: 这个跟singleTask基本上是一样,只有一个区别:在这个模式下的Activity实例所处的task中,只能有这个activity实例,不能有其他的实例。
这些启动模式可以在功能清单文件AndroidManifest.xml中进行设置,中的launchMode属性。
相关的代码中也有一些标志可以使用,比如我们想只启用一个实例,则可以使用 Intent.FLAG_ACTIVITY_REORDER_TO_FRONT 标志,这个标志表示:如果这个Activity已经启动了,就不产生新的Activity,而只是把这个Activity实例加到栈顶来就可以了。
Intent intent = new Intent(MainActivity.this, AnotherActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
Activity的加载模式受启动Activity的Intent对象中设置的Flag和manifest文件中Activity的元素的特性值相互控制。
常用的Intent Flag有:
FLAG_ACTIVITY_NEW_TASK:有Activity A,B。A通过intent跳转到B,并且这个intent添加了FLAG_ACTIVITY_NEW_TASK 标记,如果B这个Activity在Manifest.xml中的声明中添加了Task affinity(亲和性),并且和A所在的栈的affinity不同。那么系统首先会找有没有已存在的栈和B的affinity相同。如果存在则把B压进该栈中。如果B的affinity没有设置,那么B则会被直接压入A的栈中。注意如果试图从非Activity的非正常途径启动一个Activity,比如从一个Service中启动一个Activity,则intent必须要添加FLAG_ACTIVITY_NEW_TASK 标记。
FLAG_ACTIVITY_CLEAR_TOP:在一个栈中有ActivityA,B,现在A通过intent跳转至B,如果这个intent添加FLAG_ACTIVITY_CLEAR_TOP 标记,那么A,B都会被从这个栈中移除。如果没有加FLAG_ACTIVITY_CLEAR_TOP 那么,系统会重新创建一个B的实例压入到该栈中。这跟上面把B的Launch mode设置成singleTask类似。
FLAG_ACTIVITY_SINGLE_TOP:和上面Activity的 Launch mode的singleTop类似。如果某个intent添加了这个标志,并且这个intent的目标Activity就是栈顶的Activity,那么将不会新建一个实例压入栈中。
四种加载模式的区别
是否允许有多个实例:
“standard”和”singleTop”可以被实例化多次,并且是可以存在于不同的task(栈)中;这种实例化时一个task可以包括一个activity的多个实例;
“singleTask”和”singleInstance”则限制只生成一个实例,并且是task的根元素。singleTop 要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不创建新的实例。另外,“singleInstance”独占一个task,其它Activity不能存在那个task里;如果它启动了一个新的Activity,不管新的Activity的launch mode 如何,新的Activity都将会到别的task里运行(如同加了FLAG_ACTIVITY_NEW_TASK参数)。而另外三种模式,则可以和其它Activity共存。
是否每次都生成新实例
“standard”对于每一个启动Intent都会生成一个Activity的新实例;“singleTop”的activity如果在task的栈顶的话,则不生成新的该Activity的实例,直接使用栈顶的实例,否则,生成该Activity的实例。
“singleInstance”是其所在栈的唯一Activity,它会每次都被重用。
“singleTask” 如果在栈顶,则接受intent,否则,该intent会被丢弃,但是该task仍会回到前台。 当已经存在的Activity实例处理新的intent时候,会调用onNewIntent()方法,如果收到intent生成一个activity实例,那么用户可以通过back键回到上一个状态;如果是已经存在的一个activity来处理这个intent的话,用户不能通过按back键返回到这之前的状态。
所属task的区别
一般情况下,“standard”和”singleTop”的Activity的目标task,和收到的Intent的发送者在同一个task内,就相当于谁调用它,它就跟谁在同一个Task中。除非Intent包括参数FLAG_ACTIVITY_NEW_TASK。如果提供了FLAG_ACTIVITY_NEW_TASK参数,会启动到别的task里。
“singleTask”和”singleInstance” 总是把要启动的Activity作为一个task的根元素,他们不会被启动到一个其他task里。
以上。