【Android基础】Activity深入理解(二)——Activity栈和加载模式

之前的一篇文章中详细说明了Activity的生命周期,说明了Activity中的回调方法是如何被触发的。在用户使用App时,每一个 Activity 都处于某一个状态,对于开发者来说,是无法控制其应用程序处于某一个状态的,这些均由系统来完成。那么Activity的状态又按照哪种逻辑来运行的呢?这就要了解一下Activity栈。


Activity栈

每一个Activity的状态可以说都是由它所在的Activity栈中的位置所决定的。当一个新的Activity启动时,当前的正在和用户进行交互的Activity将会移动到栈顶。如果用户按后退键或者结束前台的Activity时,该Activity将会被移出栈而消亡,而此时栈顶的Activity将变为活动状态。如下图:
ActivityStack


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里。
    以上。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值