一文看懂 Android Activity 启动模式

2,720 阅读9分钟

前言:懒是人天生的惰性,明知道的不足缺,却不努力弥补。

本文大纲

Android启动模式.png

概述

在实际开发中,应该为每个Activity指定恰当的启动模式,系统中使用任务栈来存储创建的Activity实例,任务栈是一种“先进后出”的栈结构。比如,我们多次启动同一个Activity,系统会将创建的实例一次加入到任务栈中,当按back返回键时,每按一次,一个Activity出栈,直到栈空为止,然后系统回收空的任务栈。

如果Activity没有设置启动模式,你会发现多次启动一个Activity会创建多个Activity实例,浪费内存,那么怎么解决呢?Android的Activity有四种启动模式,根据不同的使用场景选择不同的启动模式,最大化降低每次都要在任务栈中创建一个新实例的压力,降低内存消耗。

任务栈先进后出原理图解:

20191202154250436.png

四种启动模式

1.standard  标准模式

standardActivity默认的启动模式,如果没有指定启动模式那么所有的Activity都是默认使用standard 标准模式。每次启动Activity都会创建一个新的实例,无论这个实例是否存在于任务栈中。

standard模式下,每当启动一个新的Activity它就会进入任务栈中,并处于栈顶的位置,对于使用standard模式下的Activity,系统不会判断该Activity在栈中是否存在,每次启动都会创建一个新的实例

比如,如果这时任务栈中有A、B、C三个Activity,启动模式都为standard,C位于栈顶(如下图一),在A、B、C三个Activity的onCreate()onDestroy()方法中都打印了log,这时再启动Activity C则会重新创建一个实例,加入任务栈中,并且位于栈顶,打印log如下:

image.png

standard 任务栈图解:

image.png

                                           图一

使用场景:一般Activity没有特殊要求都是这个模式。

2.singleTop 栈顶复用模式

singleTop模式与standard类似,不同的是如果启动的Activity已经位于栈顶,那么直接复用栈顶的Activity,不需要重新创建,否则都会在栈顶创建新的实例。

详细来说这里分三种情况:

  • 第一种:如果任务栈内没有需要启动的Activity实例,那么和standard一样,在栈顶创建Activity实例;
  • 第二种:如果任务栈内存在需要启动的Activity实例,并且该实例位于栈顶,那么直接复用该Activity(如下图二左);
  • 第三种:如果任务栈内存在需要启动的Activity实例,但是不位于栈顶,也需要重新创建Activity实例(如下图二右)。

例子:任务栈中有A、B、C三个Activity,启动模式都为singleTop,C 位于栈顶,在A、B、C三个Activity的onCreate()onNewIntent()onDestroy()方法中都打印了log;

(1)如下图二左,这时再去启动一个Activity C,如果C位于栈顶,那么会直接复用原来在栈顶的Activity C实例。只回调onNewIntent()方法,并没有被销毁(onDestroy()),打印log如下:

image.png

(2)如下图二右,这时再去启动一个Activity A,虽然任务栈中存在A的实例,但是它不在栈顶,那么还是会在任务栈创建一个新的Activity A位于栈顶,其他的Acitivity并没有销毁,打印log如下:

image.png

singleTop 任务栈图解:

image.png

                                                      图二

使用场景:适合用于接收通知启动的内容显示页面,当收到多条新闻推送时,接收该新闻的Activity设置成singleTop模式,根据传过来的Intent显示不同的新闻信息,不会启动多个Activity。

3.singleTask 栈内复用模式

单实例模式,需要创建的Activity位于任务栈中时,不会重新创建Activity,而是将该Activity上面的Activity移出任务栈,使它成为栈顶。

  • 1、如果Activity的启动模式指定为singleTask,那么每次启动Activity时,系统会先检查任务栈中是否存在该Activity的实例.
  • 2、如果存在则直接复用该Activity实例,并且将该Activity上面的所有Activity移出任务栈,使它成为栈顶;如果位于栈顶的,则直接复用该Activity(如下图三左)
  • 3、如果任务栈中没有该Activity实例则直接创建一个新的Activity实例位于栈顶(如下图三右)。

例子:任务栈中有A、B、C三个Activity,启动模式都为singleTask,C 位于栈顶,在A、B、C三个Activity的onCreate()onNewIntent()onDestroy()方法中都打印了log;

(1)如下图三左,如果再启动已经存在的Activity C并且实例在栈顶,会直接复用原来在栈顶的Activity C实例,回调onNewIntent()方法,其他的Activity没有变化,打印log如下:

image.png

(2)如下图三右,如果再启动已经存在任务栈的Activity A但是该实例不在栈顶,那么该实例A上面的ActivityC、B会被移出任务栈(被销毁),使Activity A位于栈顶并且回调onNewIntent()方法,打印log如下:

image.png

singleTask 任务栈图解:

image.png

             图三

使用场景:这种适用于应用只有一个实例,程序的入口点,比如应用首页,登录界面等。

4.singleInstance 单实例模式

全局单例模式,加强版的singleTask,具有此模式的Activity仅仅能单独位于一个任务栈中。

  • 1、如果需要整个应用中只有一个实例,可以使用singleInstance模式,与上面三种模式不同的是会启动一个新的任务栈来管理这个Activity,无论是从哪个任务栈中启动该Activity,都只会创建一个Activity实例,并且会使用一个全新的任务栈来装载这个实例。
  • 2、如果启动的Activity不存在,则会先创建一个新的任务栈,再创建该Activity实例;如果启动的Activity已经存在,那么无论位于哪一个任务栈中,都会把该Activity的任务栈调到前台,重用这个实例。

例如:启动一个A Activity,系统新开辟一个任务栈装载这个实例。

image.png

                          图四

使用场景:这个常用于系统的应用,比如Lauch、锁屏键的应用等等,整个系统中仅仅只有一个,我们一般不会用到,了解就可以了。

静态和动态方式

启动模式有静态设置和动态设置两种方式,但是两者是有差别的,一般使用静态设置的比较多,使用范围问题,静态设置无法为Activity指定FLAG_ACTIVITY_CLEAR_TOP模式,动态设置则无法为Activity指定singleInstance模式等。

1.静态XML设置

在安卓清单文件AndroidManifest.xml在Activity的跟标签设置启动模式,添加launchMode标记

        <activity
            android:name=".A_Activity"
            android:launchMode="standard" /><!--标准模式-->
        <activity
            android:name=".B_Activity"
            android:launchMode="singleTop" /><!--栈顶复用模式-->
        <activity
            android:name=".C_Activity"
            android:launchMode="singleTask" /><!--栈内复用模式-->
        <activity
            android:name=".D_Activity"
            android:launchMode="singleInstance" /><!--单例实例模式-->

2.动态设置

部分Activity特殊场景需要动态设置启动模式,通过代码Intent.addFlags()动态设置:

 Intent intent = new Intent(A_Activity.this, B_Activity.class);
 //指定B_Activity为singleTop 栈顶复用模式
 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
 startActivity(intent);

3.onNewIntent()

注意:当一个Activity设置了singleTop或者singleTask模式以后,那么生命周期则会发生改变,onCreate()方法则不会再次运行,我们一般在onCreate()方法中获取数据并做相关操作,onCreate()都是在Activity创建的时候回调了,跳转的Activity出现复用原Activity的情况,页面的数据一般是通过getIntent()来获取。那么getIntent()一直获取的是旧的数据,就无法获取最新的数据了吗?我们可以通过复写Activity的onNewIntent()方法获取最新数据。

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);//设置新的Intent,否则后面获取的Intent都是旧数据
    }

这里的Intent是最新的,需要重新setIntent(intent),设置新的Intent,否则后面获取的Intent都是旧数据。

4.Activity常用的Flags

Activity常用的Flags比较多,这里暂例几种常用的,其他的需要可以参考官方文档

  • FLAG_ACTIVITY_NEW_TASK:对应singleTask 栈内复用模式。
  • FLAG_ACTIVITY_CLEAR_TOP:如果设置,并且Activity在任务栈中,把该Activity上方的Activity都会关闭,并且这个Intent会作为新的Intent投递到老的Activity中去。一般与FLAG_ACTIVITY_NEW_TASK结合使用。
  • FLAG_ACTIVITY_RESET_TASK_IF_NEEDED:如果设置,并且该活动要么在新任务中启动,要么将现有任务放在顶部,那么它将作为任务的前门启动。这将导致应用将任务置于适当状态所需的任何关联(将活动移至或移出任务),或者在需要时将任务重置为初始状态。
  • FLAG_ACTIVITY_SINGLE_TOP:对应singleTop 栈顶复用模式。
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记位的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到Activity时,此标记位便体现了它的效果。

总结

模式说明使用场景
standard:标准模式默认模式,每次启动Activit都会创建一个新的实例,无论这个实例是否存在于任务栈中。一般Activity没有特殊要求都是这个模式
singleTop:栈顶复用模式如果实例已经位于栈顶,那么直接复用栈顶的实例,不需要重新创建,否则都会在栈顶创建新的实例。适合用于接收通知启动的内容显示页面,当收到多条新闻推送时,根据传过来的Intent显示不同的新闻信息,不会启动多个Activity
singleTask:栈内复用模式单实例模式,创建的实例位于任务栈中,不会重新创建,而是将它上面的实例移出任务栈,使它成为栈顶。这种适用于应用只有一个实例,程序的入口点,比如应用首页,登录界面等。
singleInstance:单实例模式全局单例模式,仅仅能单独位于一个任务栈中,会使用一个全新的任务栈来装载实例。这个常用于系统的应用,比如Lauch、锁屏键的应用等等,整个系统中仅仅只有一个,一般不会用到,了解即可。

点关注,不迷路


好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。我是suming,感谢各位的支持和认可,您的点赞就是我创作的最大动力,我们下篇文章见!

如果本篇博客有任何错误,请批评指教,不胜感激 !

希望我们能成为朋友,在 Github掘金 上一起分享知识,一起共勉!