Android——Activity启动模式详解

前言

Activity启动模式是android中比较基础的知识,但是我一直没怎么细看过它,好像也没怎么用到过它,不管怎么说这也算是学习android必备的知识,今天写一篇文章详细研究一下这个知识点。

Activity启动方式有四种,分别是:
standard
singleTop
singleTask
singleInstance

可以根据实际的需求为Activity设置对应的启动模式,从而可以避免创建大量重复的Activity等问题。

任务栈

在介绍这四种启动模式之前首先说一下任务栈这个概念,你可以理解为是一个Activity的集合,由于是先进先出的结构所以命名为栈,每一个Activity都存在于栈中,任务栈由TaskAffinity这个参数表示,这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需要的任务栈名字为应用的包名。当然我们可以为Activity指定TaskAffinity属性,这个属性的值不能和包名相同。(为Activity指定TaskAffinity属性只对singleTask的Activity有用) ,可以指定栈名也就意味着一个App可以由多个任务栈。不仅任务栈里的Activity有先后顺序,任务栈也有顺序,在最前面的叫前台任务栈,后面的统称为后台任务栈,后台任务栈里的Activity位于暂停状态,当前台任务栈里的Activity全部退出,这个任务栈就会销毁,跟在销毁的前台任务栈后面的任务栈就会切换到前台。

standard

标准模式,这是系统默认的启动模式,这种模式下每次启动一个Activity都会创建一个新的实例,不管这个实例是否已经存在。在这种模式下,谁启动了这个Activity,这个Activity就运行在启动他的那个Activity所在的任务栈中。举个例子:Activity A启动了Activity B(B是standard模式),那么B就会进入A所在的任务栈中。不知道你是否注意过,当我们用ApplicationContext去启动standard模式的Activity时会报错,这是因为standard模式的Activity会默认进入启动他的那个Activity所在的任务栈中,但是由于非Activity类型的ApplicationContext没有任务栈,这就会报错,如果将启动模式设为singleTask就能正常启动,具体原因看singleTask的介绍。

singleTop

栈顶复用模式,或者通俗点说就是单个栈顶,在这种模式下,如果要启动的Activity已经位于任务栈的栈顶,那么此Activity不会重新创建。但是会调用该Activity的onNewIntent(Intent intent)方法通过此方法的参数可以取出当前请求的信息。注意,如果Activity的实例已存在但是没有位于栈顶,那么新的实例仍会创建。举个例子:目前栈内的情况是ABCD,其中A位于栈低,D位于栈顶,这时再次启动D,栈内的情况依然是ABCD,只不过D调用了一次onNewIntent方法;如果我们启动的是B,那么栈内的情况就是ABCDB。

singleTask

栈内复用模式,这是一种单实例模式,也是四个启动模式中花样最多的。在这种模式下,只要Activity在一个栈中存在,那么这个Activity就不会重新创建实例,和singleTop模式一样会调用该Activity的onNewIntent(Intent intent)方法。举个例子:当一个singleTask模式的Activity请求启动,比如Activity A,系统会首先寻找是否有A想要的任务栈,如果没有系统会创建一个任务栈,然后创建A的实例放到任务栈中(这就解释了ApplicationContext可以启动singleTask的Activity的原因),如果有系统会判断栈中是否已经存在A的实例,如果存在系统就会把A调到栈顶并调用A的onNewIntent方法 (注意:把A调到栈顶后,之前那些A上面的Activity会出栈),如果栈中没有A的实例,系统就会创建A的实例并把它放入栈中。

singleInstance

单实例模式,这是一种加强的singleTask模式,他除了具有singleTask的所有特性外,还加强了一点,那就是具有此模式的Activity只能单独位于一个任务栈中。举个例子:Activity A是singleInstance模式,当A启动时系统会为他创建一个新的任务栈,然后A独自在这个任务栈中,以后如果A还想创建新的实例就不可能了。

指定启动模式和TaskAffinity

给Activity指定启动模式有两种方法:
一种是在AndroidManifest中为Activity指定启动模式:

        <activity
            android:taskAffinity="com.mss.task1"
            android:name=".Main2Activity"
            android:launchMode="singleInstance">

        </activity>

另一种是通过Intent来为Activity指定启动模式:

        Intent intent = new Intent();
        intent.setClass(MainActivity.this, SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);

这两种都能指定Activity的启动模式,但是他们还是有一些差别的,首先第二种方式的优先级要高于第一种。其次两种方式在限定范围上有所不同,比如:第一种方式无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方式无法直接为Activity指定singleInstance模式。

Intent Flags

Flags: 表示Intent的标志位,常用于Activity的场景中,它和Activity的启动模式有着密切的联系。

下面列举的是和本文主题相关的Flags属性:

Intent.FLAG_ACTIVITY_NEW_TASK

这个FLAG就相当于启动模式中的singleTask

FLAG_ACTIVITY_SINGLE_TOP

这个FLAG就相当于启动模式中的singletop,例如:原来栈中结构是A B C D,在D中启动D,栈中的情况还是A,B,C,D。

FLAG_ACTIVITY_CLEAR_TOP

这个FLAG就相当于启动模式中的SingleTask,这种FLAG启动的Activity会把要启动的Activity之上的Activity全部弹出栈空间。例如:原来栈中的结构是A B C D ,从D中跳转到B,栈中的结构就变为了A B了。(这个方法可以用来关闭多个Activity,之后的一篇博文里面会提到)

FLAG_ACTIVITY_BROUGHT_TO_FRONT

这个网上很多人是这样写的。如果activity在task存在,拿到最顶端,不会启动新的Activity。这个有可能会误导大家! 他这个FLAG其实是这个意思!比如说我现在有A,在A中启动B,此时在A中Intent中加上这个标记。此时B就是以FLAG_ACTIVITY_BROUGHT_TO_FRONT方式启动,此时在B中再启动C,D(正常启动C,D),如果这个时候在D中再启动B,这个时候最后的栈的情况是 A,C,D,B。如果在A,B,C,D正常启动的话,不管B有没有用FLAG_ACTIVITY_BROUGHT_TO_FRONT启动,此时在D中启动B的话,还是会变成A,C,D,B的。

FLAG_ACTIVITY_NO_USER_ACTION

onUserLeaveHint()作为activity周期的一部分,它在activity因为用户要跳转到别的activity而要退到background时使用。比如,在用户按下Home键,它将被调用。比如有电话进来(不属于用户的选择),它就不会被调用。
那么系统如何区分让当前activity退到background时使用是用户的选择?

它是根据促使当前activity退到background的那个新启动的Activity的Intent里是否有FLAG_ACTIVITY_NO_USER_ACTION来确定的。

注意:调用finish()使该activity销毁时不会调用该函数

FLAG_ACTIVITY_NO_HISTORY

意思就是说用这个FLAG启动的Activity,一旦退出,它不会存在于栈中,比方说!原来是A,B,C这个时候再C中以这个FLAG启动D的,D再启动E,这个时候栈中情况为A,B,C,E。

下面举一个例子,这个例子搞懂就基本掌握了启动模式:
有三个Activity:MainActivity、SecondActivity、ThirdActivity,其中MainActivity为standard模式,其他两个为singleTask模式(设置了TaskAffinity,有自己的任务栈)。现在在MainActivity中启动SecondActivity,在SecondActivity中启动ThirdActivity,在ThirdActivity又启动MainActivity,最后在MainActivity中启动SecondActivity,现在按back键,然后看到的是那个Activity?
解析:先假设MainActivity为A,SecondActivity为B,ThirdActivity为C。我们知道A为standard模式,那么A的任务栈是默认的任务栈,也就是说A的TaskAffinity为包名,我们假设为com.mss.default;而B和C的任务栈是我们设置的独立的任务栈,我们假设为com.mss.task。
开始时只有A
(任务栈里的情况是com.mss.default:A)
A启动B时,B会创建com.mss.task任务栈并入栈;
(现在的任务栈里的情况 com.mss.default:A com.mss.task:B)
B启动C时,由于C和B是相同的任务栈,所以C会直接入com.mss.task任务栈;
(现在的任务栈里的情况 com.mss.default:A com.mss.task:BC)
C启动A时,由于A是standard模式,所以系统会创建一个新的实例并把他加入到启动他的那个Activity所在的任务栈(com.mss.task)中;
(现在的任务栈里的情况 com.mss.default:A com.mss.task:BCA)
A启动B时,由于B是singleTask,而栈中已经有了B的实例,所以这时CA会出栈,B作为栈顶;
(现在的任务栈里的情况 com.mss.default:A com.mss.task:B)
现在按back键B将出栈,那么com.mss.task这个前台任务栈也没有了,com.mss.default这个后台任务栈作为了前台任务栈,现在看到的Activity就是A了;
答案就是MainActivity,你做对了吗。

补充

有没有这样的需求?跳出某个Activity之后,其作为跳板的作用完成了,自然也就没有存在的必要了(点击返回键时不想再看到它)。
这种需求除了可以在跳转之后finish这个Activity还可以使用Flag来完成,这里有两种组合的Flag可以实现相同的效果:

  1. FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK
  2. FLAG_ACTIVITY_NO_HISTORY和FLAG_ACTIVITY_NEW_TASK
    Intent intent = new Intent(this, ActivityTest.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
                      Intent.FLAG_ACTIVITY_NO_HISTORY);
    startActivity(intent);
  Intent intent = new Intent(this, ActivityTest.class);
  intent.addFlags(Intent.FLAG_ ACTIVITY_NEW_TASK | 
                            Intent.FLAG_ACTIVITY_CLEAR_TASK);
  startActivity(intent);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值