欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Activity的启动模式和任务栈

程序员文章站 2022-05-14 08:04:22
...

简介

上一篇文章详细介绍了Activity生命周期详解及注意事项。根据上一篇文章我们知道,当Activity A开启一个Activity B之后,系统会创建一个Activity B实例,推入到任务栈中,并且位于栈顶,位于Activity A的实例之上;当点击back键之后,Activity B从任务栈中被弹出即Activity B被销毁;Activity A重新可见,再次回到栈顶。

任务栈的工作原理

任务栈中的Activity永远不会重新排列,仅推入和弹出任务栈:Activity启动时被推入到任务栈;用户点击back键,当前Activity被弹出任务栈。所以,任务栈以“后进先出”(FILO)对象结构运行。
Activity的启动模式和任务栈
上图:显示任务中的每个新Activity如何向任务栈添加项目。用户按Back按钮时,当前Activity随即被销毁,而前一个Activity 恢复执行。

如果用户继续按Back键,任务栈中的相应Activity就会弹出,以显示前一个Activity,当所有Activity均从任务栈中移除后,任务即不复存在。

Activity 和任务的默认行为总结如下:

  • 当 Activity A 启动 Activity B 时,Activity A 将会停止,但系统会保留其状态(例如,滚动位置和已输入表单中的文本等相关信息)。如果用户在处于 Activity B 时按“返回”按钮,则 Activity A 将恢复其状态,继续执行。

  • 用户通过按Home键离开任务时,当前 Activity 将停止且其任务会进入后台。 系统将保留任务中每个 Activity 的状态。如果用户稍后通过选择开始任务的启动器图标来恢复任务,则任务将出现在前台并恢复执行堆栈顶部的 Activity。

  • 如果用户按Back键,则当前 Activity 会从堆栈弹出并被销毁。 堆栈中的前一个 Activity 恢复执行。销毁 Activity 时,系统不会保留该 Activity 的状态。

启动模式

正常情况下,每次启动一个Activity都会在任务栈中加入对应的Activity实例,但是有没有特殊情况下,开启Activity不会再次创建同一个Activity实例呢?

现在我们来详细介绍下Activity的四种启动模式,分别为standard,singleTop,singleTask,singleInstance四种模式;

  • standard:
    默认模式。每次启动该Activity都会创建一个实例,添加到任务栈中,不管任务栈中是否已经存在该Activity实例。

  • singleTop
    singleTops是栈顶只存在一个该Activity实例
    分为两种情况:
    1 如果当前任务栈的顶部已存在该Activity的实例,则系统会通过调用该实例的onNewIntent()方法向其传送Intent,而不是创建Activity的新实例;
    2 如果当前任务栈的顶部不存在该Activity的实例,和standard模式一样创建一个该Activity的实例,并推入到栈顶。

例如:Activity C的launchMode =“singleTop”;假如当前任务栈(A-B—C),A位于根,C位于栈顶,再次启动Activity C,则Activity C的现有实例会通过onNewIntent()接收Intent,因为它位于任务栈的顶部,不在会创建Actiity C的实例;

如果当前任务栈(A-C-B),B位于栈顶,再次启动Activity C会创建Activity C的实例,并推入到任务栈。任务栈的实例顺序为A—C-B-C;

  • singleTask
    singleTask是该任务栈只存在一个该Activity实例,属于单例模式;
    和singleTop类似也是分为两种情况;
    1 当前任务栈中已经存在该Activity的实例的情况下,系统会通过该实例的onNewIntent()方法向其传送Intent,不在创建Activity的新实例;此时又分为两种情况
    1.1 如果当前Activity的实例位于栈顶,则和singleTop一样,不再有其他操作;
    1.2 如果当前Activity的实例不在栈顶的情况下,会将该实例上面的其他Activity实例全部弹出当前任务栈,该Activity的实例位于栈顶;
    因此singleTask模式具有clearTop的作用
    2 当前任务栈中不存在该Activity的实例时,和standard模式一样创建一个该Activity的实例,并推入到栈顶;

例如:Activity C的launchMode =“singleTask”;假如当前任务栈(A-B—C),A位于根,C位于栈顶,再次启动Activity C,则Activity C的现有实例会通过onNewIntent()接收Intent,因为它位于任务栈的顶部,不在会创建Actiity C的实例;

如果当前任务栈(A-C-B),B位于栈顶,再次启动Activity C系统会Activity B的实例弹出任务栈,将Activity C的实例置于栈顶,任务栈的实例顺序为A—C;

当前任务栈(A-B),没有Activity C的实例,启动Activity C系统会创建C的实例,并弹入任务栈的顶部,任务栈的实例顺序为A-B-C;

  • singleInstance 和singleTask类似,也是属于单例模式,不同点在于singleInstance模式的Activity启动会单独存在于一个任务栈中,并且该任务栈中有且只有这一个Activity实例,不和其他activity共存在一个任务栈。如果多个不同Activity的launchMode采用singleInstance启动模式,系统会为每一个Activity创建一个任务栈,将Activity实例推入到对应的任务栈中。

先在有四个Activity分别为MainActivity,SecondActivityThridActivity,FourthActivity;其中MainActivity和ThridActivity的launchMode为standard;SecondActivity和FourthActivity的launchMode为singleInstance,依次把四个Activity启动。
通过adb shell dumpsys activity activities 命令行查看当前应用的任务栈,会发现
Activity的启动模式和任务栈
上图 #10064表示任务栈Id,sz:任务栈类的Activity实例数量
从上图可以看出,MainActivity和ThridActivity在同一个任务栈中,SecondActivity和FourthActivity分别位于不同的任务栈;

Intent标志

启动 Activity 时,您可以通过在传递给startActivity()的Intent中加入相应的标志,修改 Activity 与其任务的默认关联方式。可用于修改默认行为的标志包括:

  • FLAG_ACTIVITY_NEW_TASK在新任务中启动 Activity。如果已为正在启动的 Activity 运行任务,则该任务会转到前台并恢复其最后状态,同时 Activity 会在 onNewIntent() 中收到新 Intent。正如前文所述,这会产生与 “singleTask”launchMode 值相同的行为。

  • FLAG_ACTIVITY_SINGLE_TOP如果正在启动的Activity是当前Activity(位于返回栈的顶部),则 现有实例会接收对 onNewIntent() 的调用,而不是创建Activity的新实例。
    正如前文所述,这会产生与”singleTop”launchMode值相同的行为。

  • FLAG_ACTIVITY_CLEAR_TOP
    如果正在启动的 Activity已在当前任务中运行,则会销毁当前任务顶部的所有Activity,并通过 onNewIntent()将此Intent传递给Activity已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例。产生这种行为的launchMode属性没有值。
    LAG_ACTIVITY_CLEAR_TOP通常与FLAG_ACTIVITY_NEW_TASK结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent的位置。

通过以下两种方式使用Intent Flags

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

addFlags:添加额外的Flags到Intent中;add相当于添加另外一种情况;
setFlags: 设置Flags来控制Intent的处理;set相当于替换之前的Flags;

taskAffinity

taskAffinity可以翻译为任务相关性,关联性;
“关联”指示Activity优先属于哪个任务。默认情况下,同一应用中的所有Activity彼此关联。因此,默认情况下,同一应用中的所有Activity优先位于相同任务中,也可以修改Activity的默认关联。taskAffinity属性取字符串值,默认情况下为应用的包名;但是也可以给Activity指定任务关联,通过taskAffinity设置任务关联的名称,可以为任意字符串,是必须至少包含一个”.”,否则会报错;

在两种情况下,关联会起作用:
- 启动 Activity 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 标志。

android:launchMode="singleTask"
android:taskAffinity="frameworkdemo.test"

通过上述方法,可以将Activity放到单独的任务栈中,任务栈的名称为taskAffinity指定;

将SecondActivity设置为android:launchMode=”singleTask”android:taskAffinity=”com.good.things”FourthActivity设置android:launchMode=”singleTask”android:taskAffinity=”frameworkdemo.test”,现在来看下结果:
Activity的启动模式和任务栈
上图中椭圆的内容表示任务栈Id,矩形表示任务栈的名称,即taskAffinity的指定名称;

  • Activity将其allowTaskReparenting属性设置为 “true”。
    allowTaskReparenting用来标记Activity能否从启动的Task移动到taskAffinity指定的Task,当把Activity的allowTaskReparenting属性设置成true时,Activity就拥有了一个转移所在Task的能力。具体点来说,就是一个Activity现在是处于某个Task当中的,但是它与另外一个Task具有相同的affinity值,那么当另外这个任务切换到前台的时候,该Activity就可以转移到现在的这个任务当中。allowTaskReparenting默认是继承至application中的allowTaskReparenting=false,如果为true,则表示可以更换;false表示不可以。

例如:ThridActivity设置为 android:allowTaskReparenting=”true”,android:taskAffinity=”allowTaskReparenting.test”,SecondActivity设置为 android:launchMode=”singleTask”,android:taskAffinity=”com.good.things”,现在SecondActivity开启ThridActivity;看下结果:
Activity的启动模式和任务栈
由上图可知,ThridActivity转移了Task;

思考

如果有两个任务栈A,B;A中有两个Activity,FirstActi
vity,SecondActivity,firstActivity-secondActivity(栈顶),并且lauchMode都是singleTask,B中有thridActiivty,现在思考下1) thridActivity开启SecondActivity之后,依次回退,先后顺序?2)thridActivity开启FirstActivity之后,依次回退,先后顺序?

  • 问题一:SecondActivity – FirstActivity – ThridActivity;
  • 问题二:FirstActivity – ThridActivity;因为single

参考文档

Google开发文档
任玉刚《Android开发艺术探讨》

相关标签: Activity