ActivityLaunchMode

在默认情况下的我们多次打开同一个Activity时,系统会创建多个实例并把它们一一放入任务栈中,当我们单击back键,会发现这些Activity会一一回退。任务栈是一种”后进先出“的栈结构,这个比较好理解,每按一下back键就会有一个Activity出栈,直到栈空为止,当栈中无任何Activity的时候,系统就会回收这个任务栈。知道了Android默认的启动模式后我们就会发现一个问题:多次启动同一个Activity,系统重复创建多个实例,这样不是很傻吗?这样做的确有点傻,Android在设计的时候不可能不考虑这个问题,所以它提供了启动模式来修改系统的默认行为。目前有四种启动模式:standard、singleTop、singleTask和singleInstance

启动模式

standard

标准模式,系统默认的模式。每次启动一个Activity都会重新建立一个新的实例,不管这个实例是否已经存在。被创建的实例的生命周期符合典型情况下Activity的生命周期,如上节描述,它的onCreate()、onStart()、onResume()都会被调用。这是一种典型的多实例实现,一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中,

singleTop

栈顶复用模式。在这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。需要注意的是,这个Activity的onCreate()、onStart()不会被系统调用,因为他并没有发生改变。如果新Activity的实例已经存在但不是位于栈顶,那么新Activity仍然会重新建立。举个例子,假设目前栈内的情况为ABCD,其中ABCD为四个Activity,A位于栈底,D位于栈顶,这个时候假设要再次启动D,如果D的启动模式为singleTop,那么栈内的情况就为ABCD;如果D的启动模式为standard,那么由于D被重新创建,导致栈内的情况就变为ABCDD

singleTask

栈内复用模式。这是一种单实例模式,在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会被重新创建实例,和singleTop模式一样,系统也会回调其onNewIntent。具体一点,当一个具有singleTask模式的Activity请求启动后,比如Activity A,系统首先会寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例后把A放入栈中。如果存在A所需要的任务栈,这时要看A是否在栈中有实例存在,如果有实例存在,那么系统就会把A调到栈顶并调用它的onNewIntent方法,如果实例不存在,就创建A的实例并把A压入栈中

  1. 比如当前任务栈S1中的情况为ABC,这个时候ActivityD以singleTask模式请求启动,其所需要的任务栈为S2,由于S2和D的实例均不存在,所以系统会先创建任务栈S2,然后再创建D的实例并将其入栈到S2
  2. 另一种情况是,假设D所需要的任务栈是S1,其他情况如上面例子1所示,那么由于S1已经存在,所以系统会直接创建D的实例并将其入栈到S1
  3. 如果所需要的任务栈为S1,并且当前任务栈S1的情况为ADBS,根据任务栈内复用原则,此时D不会重新创建,系统会把D切换到栈顶并调用其onNewIntent方法,且会导致栈内所有在D上面的Activity全部出栈,于是最终S1中的情况为AD

singleInstance

单实例模式。这是一种加强的singleTask模式,它除了具有singleTask模式的所有特性外,还加强了一点,那就是具有此模式的Activity只能单独的位于一个任务栈中,换句话说,比如Activity A是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中,由于栈内复用的特性,后续的请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了

无论 Activity 是在新任务中启动,还是在与启动 Activity 相同的任务中启动,用户按“返回”按钮始终会转到前一个 Activity。 但是,如果启动指定 singleTask 启动模式的 Activity,则当某后台任务中存在该 Activity 的实例时,整个任务都会转移到前台。此时,返回栈包括上移到堆栈顶部的任务中的所有 Activity

如何给Activity指定启动模式

  1. 通过AndroidMenifest为Activity指定启动模式,如下所示
    1
    2
    3
    4
    <acticity
    android:name="com.test.activity"
    android:launchMode="singleTask"
    android:label="@string/app_name"/>
  2. 通过在Intent中设置标志位来为Activity指定启动模式,例如:
    1
    2
    3
    Intent i = new Intent(this, OtherActivity.class);
    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(i);

这来两种方式都可以为Activity指定启动模式,但是二者还是有区别的。首先优先级上第二种方式的优先级要高于第一种,当两种同时存在时,以第二种方式为准;其次,上述两种方式在限定范围上有所不同,比如,第一种方式无法直接为Activit设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方式无法为Activity指定singleInstance模式

通过adb命令获取Activity栈

  • 配置adb环境
    1. 找到SDK安装目录,并找到platform-tools和tools两个文件夹
    2. 将找到的两个文件夹的路径配置到环境变量中,如下图所示,最后在Path环境变量参数中加入%adb%

  • 打开cmd窗口,执行adb shell dumpsys activity即可显示现在的Activity栈的情况

内容补充

如果有两个Activity B、C是singleTask启动模式,此时从A Activity启动B,再从B启动C,此时的栈中情况为ABC,最后再从C启动B,那么此时的Activity栈中的情况是什么呢?答案是AB,因为此时栈中已经有B了,在不新建B的情况下要把B移动到栈顶唯一的办法只有将C移出栈了,验证如下图所示:

Activity的Flags

  • FLAG_ACTIVITY_NEW_TASK
    这个标记位的作用是为Activity指定”singleTask”启动模式,其效果和在XML中指定该启动模式相同
  • FLAG_ACTIVITY_SINGLE_TOP
    这个标记位的作用是为Activity指定”singleTop”启动模式,其效果和在XML中指定该启动模式相同
  • FLAG_ACTIVITY_CLEAR_TOP
    具有此标记位的Activity,当他活动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般会和singleTask启动模式一起出现,在这种情况下,被启动的Activity的实例如果已经存在,那么系统就会调用它的onNewIntent()。如果被启动的Activity采用standard模式启动,那么它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈顶,通过前面的补充内容,我们已经能知道了singleTask模式默认就具有此标记位的效果
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    具有此标记位的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于在XML中指定Activity的属性android:excludeFromRecents="true"

one more thing

本文测试代码地址:https://github.com/SMILEORIGIN/ActivityStartModel

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×