Activity的启动模式

本文结构:

ActivityLaunchMode

前言:介绍Activity的启动模式之前先介绍一下任务栈。任务栈是一个“后进先出”的栈结构,每按一下back键就有一个Activity出栈,直到栈空为止,当栈中无任何Activity的时候,系统就会回收这个任务栈。

1.1 Activity的LaunchMode

Activity的LaunchMode目前有四种启动模式:standard、singleTop、singleTask以及singleInstance。

  1. standard:标准模式,也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在。这种模式是典型的多实例实现,一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity的所在的栈中。

注意:当我们用ApplicationContext去启动standard模式的Activity的时候就会报错,log如下:

1
...Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag....

这是因为standard模式下的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的context并没有任务栈,所以解决这个问题的办法是为待启动Activity指定FLAG_ACTIVITY_NEW_TASK标记位。这个时候待启动Activity实际上是以singleTask模式启动的。

  1. singleTop:栈顶复用模式。在这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们就可以取出当前请求的信息;如果新Activity的实例已经存在但不是位于栈顶,那么新Activity会被重新创建。
  2. singleTask:栈内复用模式。这是一种单实例模式,在这种模式下,只要Activity在一个栈中存在,那么多次启动这个Activity都不会重新创建实例,同时系统会回调其onNewIntent。具体一点,当一个具有singleTask模式的Activity请求启动后,系统会首先寻找是否存在这个Activity想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建这个Activity的实例后把这个Activity放入栈中,如果存在,这是要看这个Activity是否在栈中有实例存在,如果有实例存在,那么系统会把这个Activity调到栈顶并调用它的onNewIntent,如果实例不存在,就创建这个Activity的实例并将其压入栈中。
    例子:如果D所需的任务栈为S1,并且当前S1的情况为ADBC,根据栈内复用原则,此时D不会被重新创建,系统会把D切换到栈顶并调用其onNewIntent,同时由于singleTask默认默认具有clearTop的效果,会导致栈内所有在D上面的Activity全部出栈,最终S1中的情况为AD。
  3. singleInstance:单实例模式,加强版的singleTask模式,它除了具有singleTask的所有特性外,还加强了一点,那就是此模式下的Activity只能单独的位于一个任务栈中。

什么是Activity所需要的任务栈?这要从一个参数说起:TaskAffinity,任务相关性。这个参数标识了一个Activity所需要的任务栈的名字,默认情况下所有Activity所需的任务栈的名字为应用的包名,但是我们可以为每个Activity都单独指定TaskAffinity属性,这个属性值不能和包名一样,否则就相当于没有指定。TaskAffinity属性主要和singleTask启动模式和allowTaskReparenting属性配对使用,在其他情况下没有意义。

1.2 如何给Activity指定启动模式

  1. 通过AndroidMenifest为Activity指定启动模式。

    1
    2
    3
    4
    5
    <activity
    android:name="com.my.test.TestActivity"
    android:launchMode="singleTask"
    android:label="@string/app_name">
    </activity>
  2. 通过在Intent设置标志位来为Activity指定启动模式

    1
    2
    3
    Intent intent = new Intent(this, TestActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);

二者区别:首先,优先级上第二种高于第一种,当两种同时存在时,以第二种为准;其次,二者在限定范围上有所不同。

1.3 Activity的Flags

  1. FLAG_ACTIVITY_NEW_TASK:这个标记位的作用是为Activity指定“singleTask”启动模式。
  2. FLAG_ACTIVITY_SINGLE_TOP:这个标记位的作用是为Activity指定“singleTop”启动模式。
  3. FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈,这个标记位一般和singleTask启动模式一起出现。在这种情况下,如果被启动的实例已经存在,那么系统会调用它的onNewIntent。