Activity的启动模式

Author Avatar
罗炜光 7月 10, 2016
  • 在其它设备中阅读本文章

Activity的启动模式

可以使用adb shell dumpsys activity命令查看activity的任务栈

standard

  1. 标准模式,也是系统的默认模式。
  2. 每次启动Activity都会创建一个实例,不管实例是否存在。
  3. 一个任务栈可以有多个实例,每个实例也可以属于不同的任务
  4. 谁启动了此Activity,那么这个Activity就运行在启动它的那个Activity栈
  5. 使用ApplicationContext去启动standard模式的Activity会报错,因为非Activity类型的Context并没有任务栈
  6. 被创建的生命周期符合典型情况

singleTop

  1. 栈顶复用模式
  2. 如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建
  3. 如何不位于栈顶则会重新创建
  4. 当Activity不会重新创建时,会回调onNewIntent方法
  • 位于栈顶时

  • 不在栈顶时

singleTask

  1. 栈内复用模式
  2. 当一个栈内不存在时,会创建一个Activity
  3. 当一个栈内存在时,则会将此Activity在栈内位置以上的Activity全部出栈,并调用onNewIntent方法
  4. 当Activity所在的栈不存在时,则会先创建一个任务栈,再创建Activity
  • 栈内存在时

  • 栈内不存在时

singleInstance

  1. 单实例模式,一种加强的singleTask模式
  2. 具有singleTask所以的特性
  3. 当Activity启动时,系统会为它创建一个新的任务栈

跨应用调用

standard与singleTop

  • 5.0之前
    跨应用之间启动Activity,新生成的Activity会放入发送Intent的Task的栈的顶部

  • 5.0之后
    跨应用之间启动Activity,会创建一个新的Task,新生成的Activity就会放入刚创建的Task中

singleTask

  • 在跨应用Intent传递时,如果系统中不存在singleTask Activity的实例,那么讲创建一个新的Task,然后创建SingleTask Activity的实例,将其放入新的Task中
  • 如果singleTask Activity所在的应用进程存在,但是singleTask Activity实例不存在,那么从别的应用启动这个Activity,新的Activity实例会被创建,并放入到所属进程所在的Task中,并位于栈顶位置
  • 更复杂的一种情况,如果singleTask Activity实例存在,从其他程序被启动,那么这个Activity所在的Task会被移到顶部,并且在这个Task中,位于singleTask Activity实例之上的所有Activity将会被正常销毁掉。如果我们按返回键,那么我们首先会回退到这个Task中的其他Activity,直到当前Task的Activity回退栈为空时,才会返回到调用者的Task

    singleInstance

  • 这个模式和singleTask差不多,因为他们在系统中都只有一份实例。唯一不同的就是存放singleInstance Activity实例的Task只能存放一个该模式的Activity实例。如果从singleInstance Activity实例启动另一个Activity,那么这个Activity实例会放入其他的Task中。同理,如果singleInstance Activity被别的Activity启动,它也会放入不同于调用者的Task中。

taskAffinity属性

在某些情况下,Android需要知道一个Activity属于哪个Task,即使它没有被启动到一个具体的Task里。这是通过任务共用性(Affinities)完成的。任务共用性(Affinities)为这个运行一个或多个Activity的Task提供了一个独特的静态名称,默认的一个活动的任务共用性(Affinity)是实现了该Activity的.apk包的名字。

当开始一个没有 Intent.FLAG_ACTIVITY_NEW_TASK标志的Activity时,任务共用性affinities不会影响将会运行该新活动的 Task:它总是运行在启动它的Task里。但是,如果使用了NEW_TASK标志,那么共用性(affinity)将被用来判断是否已经存在一个有相同共用性(affinity)的Task。如果是这样,这项Task将被切换到前面而新的Activity会启动于这个Task的顶层。

这种特性在您必须使用NEW_TASK标志的情况下最有用,尤其是从状态栏通知或桌面快捷方式启动活动时。结果是,当用户用这种方式启动您的应用程序时,它的当前Task将被切换到前台,而且想要查看的Activity被放在最上面。
你可以在程序清单(Manifest)文件的应用程序application标签中为.apk包中所有的活动分配你自己的任务共用性Affinites,或者在活动标记中为各个活动进行分配。

一些说明其如何使用的例子如下:

  • 如果您的.apk包含多个用户可以启动的高层应用程序,那么您可能需要对用户看到的每个Activity(活动)指定不同的affinities。一个不错的命名惯例是以附加一个以冒号分隔的字符串来扩展您的.apk包名。例如,“ com.android.contacts ”.apk可以有affinities:“com.android.contacts:Dialer”和“ com.android.contacts:ContactsList”。
  • 如果您正在替换一个通知,快捷方式,或其他可以从外部发起的应用程序的“内部”活动,你可能需要明确设定您替代活动的taskAffinity和您准备替代的应用程序一样。例如,如果您想替换contacts详细信息视图(用户可以创建并调用快捷方式),你得把taskAffinity设置成“com.android.contacts”。

跟Task有关的 manifest文件中Activity的特性值介绍

android:allowTaskReparenting

用来标记Activity能否从启动的Task移动到有着affinity的Task(当这个Task进入到前台时)

“true”,表示能移动,“false”,表示它必须呆在启动时呆在的那个Task里。

如果这个特性没有被设定,设定到元素上的allowTaskReparenting特性的值会应用到Activity上。默认值为“false”。

一般来说,当Activity启动后,它就与启动它的Task关联,并且在那里耗尽它的整个生命周期。当当前的Task不再显示时,你可以使用这个特性来强制Activity移动到有着affinity的Task中。典型用法是:把一个应用程序的Activity移到另一个应用程序的主Task中。
例如,如果 email中包含一个web页的链接,点击它就会启动一个Activity来显示这个页面。这个Activity是由Browser应用程序定义的,但是,现在它作为email Task的一部分。如果它重新宿主到Browser Task里,当Browser下一次进入到前台时,它就能被看见,并且,当email Task再次进入前台时,就看不到它了。

Actvity的affinity是由taskAffinity特性定义的。Task的affinity是通过读取根Activity的affinity 决定。因此,根Activity总是位于相同affinity的Task里。由于启动模式为“singleTask”和“singleInstance” 的Activity只能位于Task的底部,因此,重新宿主只能限于“standard”和“singleTop”模式。

android:alwaysRetainTaskState

用来标记Activity所在的Task的状态是否总是由系统来保持。

“true”,表示总是;“false”,表示在某种情形下允许系统恢复Task到它的初始化状态。默认值是“false”。

这个特性只针对Task的根Activity有意义;对其它Activity来说,忽略之。
一般来说,特定的情形如当用户从主画面重新选择这个Task时,系统会对这个Task进行清理(从stack中删除位于根Activity之上的所有Activivity)。典型的情况,当用户有一段时间没有访问这个Task时也会这么做,例如30分钟。
然而,当这个特性设为“true”时,用户总是能回到这个Task的最新状态,无论他们是如何启动的。这非常有用,例如,像Browser应用程序,这里有很多的状态(例如多个打开的Tab),用户不想丢失这些状态。

android:clearTaskOnLaunch

用来标记是否从Task中清除所有的Activity,除了根Activity外(每当从主画面重新启动时)

“true”,表示总是清除至它的根Activity,“false”表示不。默认值是“false”。

这个特性只对启动一个新的Task的Activity(根Activity)有意义;对Task中其它的Activity忽略

当这个值为“true”,每次用户重新启动这个Task时,都会进入到它的根Activity中,不管这个Task最后在做些什么,也不管用户是使用 BACK还是HOME离开的。当这个值为“false”时,可能会在一些情形下(参考alwaysRetainTaskState特性)清除Task的 Activity,但不总是。

假设,某人从主画面启动了Activity P,并从那里迁移至Activity Q。接下来用户按下HOME,然后返回Activity P。一般,用户可能见到的是Activity Q,因为它是P的Task中最后工作的内容。然而,如果P设定这个特性为“true”,当用户按下HOME并使这个Task再次进入前台时,其上的所有的 Activity(在这里是Q)都将被清除。因此,当返回到这个Task时,用户只能看到P。

如果这个特性和allowTaskReparenting都设定为“true”,那些能重新宿主的Activity会移动到共享affinity的Task中;剩下的Activity都将被抛弃,如上所述。

android:finishOnTaskLaunch

用来标记当用户再次启动它的Task(在主画面选择这个Task)时已经存在的Activity实例是否要关闭(结束)

“true”,表示应该关闭,“false”表示不关闭。默认值是“false”。
如果这个特性和allowTaskReparenting都设定为“true”,这个特性胜出。Activity的affinity忽略。这个Activity不会重新宿主,但是会销毁。

android:launchMode
用于指示Activity如何启动。这里有四种模式,与Intent对象中的Activity Flags(FLAGACTIVITY*变量)共同作用,来决定Activity如何启动来处理Intent。

android:noHistory

用于标记当用户从Activity上离开并且它在屏幕上不再可见时Activity是否从Activity stack中清除并结束(调用finish()方法)——“true”,表示它应该关闭,“false”,表示不需要。默认值是“false”。

“true”值意味着Activity不会留下历史痕迹。因为它不会在Activity stack的Task中保留,因此,用户不能返回它。

比如启用界面的就可以借用这个。

android:taskAffinity

Activity为Task拥有的一个affinity。拥有相同的affinity的Activity理论上属于相同的Task(在用户的角度是相同的“应用程序”)。Task的affinity是由它的根Activity决定的。

affinity决定两件事情——Activity重新宿主的Task(参考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK标志启动的Activity宿主的Task。

默认情况,一个应用程序中的所有Activity都拥有相同的affinity。捏可以设定这个特性来重组它们,甚至可以把不同应用程序中定义的Activity放置到相同的Task中。为了明确Activity不宿主特定的Task,设定该特性为空的字符串。

如果这个特性没有设置,Activity将从应用程序的设定那里继承下来(参考元素的taskAffinity特性)。应用程序默认的affinity的名字是元素中设定的package名。

参考资料

Android开发艺术探索
深入讲解Android中Activity launchMode
Task Affinity