精讲任务栈和Activity启动模式

任务栈

栈root,栈顶。

 

设备主屏幕是大多数任务的起点。当用户触摸应用启动器中的图标(或主屏幕上的快捷方式)时,该应用的任务将出现在前台。如果应用不存在任务(应用最近未曾使用),则会创建一个新任务,并且该应用的"主"Activity 将作为堆栈中的根 Activity 打开。

当前 Activity 启动另一个 Activity 时,该新 Activity 会被推送到堆栈顶部,成为焦点所在。前一个 Activity 仍保留在堆栈中,但是处于停止状态。Activity 停止时,系统会保持其用户界面的当前状态。用户按"返回"按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行(恢复其 UI 的前一状态)。

如果用户继续按"返回",堆栈中的相应 Activity 就会弹出,以显示前一个 Activity,直到用户返回主屏幕为止(或者,返回任务开始时正在运行的任意 Activity)。当所有 Activity 均从堆栈中移除后,任务即不复存在。

 

安卓系统管理着不同模式下的多个ActivityStack(比如在home launcher界面需要有一个ActivityStack,画中画模式,分屏模式等),一个ActivityStack可以包含很多个TaskRecord,一个TaskRecord又可以包含很多个ActivityRecord。

每一个ActivityRecord都会有一个Activity与之对应,一个Activity可能会有多个ActivityRecord,因为Activity可以被多次实例化,取决于其launchmode。一系列相关的ActivityRecord组成了一个TaskRecord,TaskRecord是存在于ActivityStack中,ActivityStackSupervisor是用来管理这些ActivityStack的。

下面是一个简单的关系图:

 

launcher也有自己的task,

系统是根据task进行管理的,而不是ActivityStack。

task又分为前台task和后台task,前台task(也叫当前task)就是栈顶是和用户交互的activity的task,后台task就是非前台task。

当一直按back按键回退时,如果发现已经回退到launcher时就不会再继续回退。

taskAffinity

清单文件中Application和Activity标签都可以使用android:taskAffinity,值为String类型。

它代表这个Activity所希望归属的Task,在默认情况下,同一个app中的所有Activity拥有共同的Affinity,即manifest中定义的package。

另外task的affinity取决于它的根部Activity。

 

如果使用SingleInstance模式启动的Activity,如果没有指定affinity的话,创建的新task的affinity还是app的包名。

 

taskAffinity在两种情况下起作用:

  • 当启动Activity的Intent中带有FLAG_ACTIVITY_NEW_TASK标志时。

在默认情况下,目标Activity将与startActivity的调用者处于同一task中。但如果用户特别指定了FLAG_ACTIVITY_NEW_TASK,表明它希望为Activity重新开设一个Task。这时就有两种情况:

  1. 假如当前已经有一个Task,它的affinity与新Activity是一样的,那么系统会直接用此Task来完成操作,而不是另外创建一个Task;

  2. 否则系统需要创建一个Task。

 

  • 当Activity中的allowTaskReparenting属性设置为true时。

在这种情况下,Activity具有"动态转移"的能力。举个前面的"短信"例子,在默认情况下,该应用程序中的所有Activity具有相同的affinity。

当另一个程序启动了"短信编辑"时,一开始这个Activity和启动它的Activity处于同样的Task中。但如果"短信编辑"Activity指定了allowTaskReparenting,且后期"短信"程序的Task转为前台,此时"短信编辑"这一Activity会被"挪"到与它更亲近的"短信"Task中。

启动模式

在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作。在Android中Activity的启动模式决定了Activity的启动运行方式。

有两种方式来声明指定启动模式:

  1. 在清单文件中

  2. 在java代码中

 

如果 Activity A 启动 Activity B,则 Activity B 可以在其清单文件中定义它应该如何与当前任务关联(如果可能),并且 Activity A 还可以请求 Activity B 应该如何与当前任务关联。

如果这两个方式均定义 Activity B 应该如何与任务关联,则 Activity A 的请求(Intent 中所定义)优先级要高于 Activity B 的请求(其清单文件中所定义)。

 

在清单文件中

不管何种方式启动Activity,被启动的Activity通常都要位于ActivityStack的栈顶。

 

Activity启动方式launchMode,在清单文件中配置。

<activity android:name=".MainActivity"
android:launchMode="xxx" />

您可以分配给 launchMode 属性的启动模式共有四种:

 

"standard"(默认模式)

默认。系统在启动这个 Activity 的任务中创建 此Activity 的新实例,并向其传送 Intent。

Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个该activity实例。

 

"singleTop"

单独使用时,只有当 当前任务栈的顶部已存在这个 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建 Activity 的新实例。如果当前任务栈顶不是这个Activity则会创建新的实例。

例如,假设任务堆栈是 A-B-C-D;D 位于顶部。收到针对 D 类 Activity 的 Intent,

  • 如果 D 具有默认的 "standard" 启动模式,则会启动该类的新实例,且堆栈会变成 A-B-C-D-D。

  • 如果 D 的启动模式是 "singleTop",则 D 的现有实例会通过 onNewIntent() 接收 Intent,因为它位于堆栈的顶部;而堆栈仍为 A-B-C-D。

  • 如果收到针对 B 类 Activity 的 Intent,则会向堆栈添加 B 的新实例,即便其启动模式为 "singleTop" 也是如此。

注:为某个 Activity 创建新实例时,用户可以按"返回"按钮返回到前一个 Activity。但是当一个已经存在的Activity通过onNewIntent()在处理新的intent时,用户是不能通过返回键 回到这个Activity传入新intent之前的状态的。

 

"singleTask"

希望创建新任务并实例化Activity,当然会受到taskAffinity的影响,即如果已经有一个和要启动的Activity的taskAffinity相同的task,那么就在这个task中创建实例,相反就是创建一个新任务。

但是,如果该 Activity 的一个实例已存在于一个的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是创建新实例。此Activity只能存在一个实例。

注:尽管 Activity 在新任务中启动,但是用户按"返回"按钮仍会返回到前一个 Activity。

 

另外无论 Activity 是在新任务中启动,还是在与启动 Activity 相同的任务中启动,用户按"返回"按钮始终会转到前一个 Activity。但是,如果启动指定 singleTask 启动模式的 Activity,则当某后台任务中存在该 Activity 的实例时,整个任务都会转移到前台。图中显示了这种情况。

 

 

"singleInstance".

与 "singleTask" 类似,只是系统不会将任何其他 Activity 启动到此Activity实例所在的任务栈中。该 Activity 始终是其任务栈唯一仅有的成员;由此 Activity 启动的任何 Activity 均在其他的任务栈中打开。

一旦该模式的Activity的实例存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例,其效果相当于多个应用程序共享一个应用,不管谁激活该Activity都会进入同一个应用中。

 

singleTask/singleInstance的特别说明

从ActivityStack源码可以看出,singleTask/singleInstance模式会clearTop的效果,

} else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {

 

从ActivityStack源码可以看出,singleTask/singleInstance模式会自动添加FLAG_ACTIVITY_NEW_TASK

} else if (mLaunchSingleInstance || mLaunchSingleTask) {
    // The activity being started is a single instance...  it always
    // gets launched into its own task.
    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}

 

使用 Intent 标志

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

  • FLAG_ACTIVITY_NEW_TASK

和"singleTask"类似,只不过单独使用是没有clearTop效果的。

与启动的那个activity的Affinity相同的任务不存在的话,就创建一个task并移到前台,并把启动的那个activity的新实例加入栈顶(此时应该是该task的root activity)。

与启动的那个activity的Affinity相同的任务已存在,会把此任务提到前台,但是分几种情况:

  1. 启动的那个activity是默认模式,会把此任务提到前台,然后在此任务栈顶加入此activity的新实例。

  2. 启动的那个activity是singleTop模式,会把此任务提到前台,然后如果任务栈顶是此activity的话就直接复用并调用onNewIntent,否则在此任务栈顶加入此activity的新实例。

  3. 启动的那个activity的Intent加入FLAG_ACTIVITY_CLEAR_TOP,会把此任务提到前台,然后如果任务栈中有此activity实例的话就执行FLAG_ACTIVITY_CLEAR_TOP操作,否则在此任务栈顶加入此activity的新实例

 

当调用者startActivityForResult时,不能使用此标志。

有关禁用此行为的标志,请参见FLAG_ACTIVITY_MULTIPLE_TASK。

 

 

  • FLAG_ACTIVITY_SINGLE_TOP

这会产生与 "singleTop"launchMode 值相同的行为。

If set, the activity will not be launched if it is already running at the top of the history stack.

 

  • FLAG_ACTIVITY_CLEAR_TOP

单独使用时,只有当正在启动的 Activity 已在当前任务中运行,则会销毁当前任务顶部的所有 Activity,并通过 onNewIntent() 将此 Intent 传递给 Activity 已resumed 的实例(现在位于顶部),而不是启动该 Activity 的新实例。

FLAG_ACTIVITY_CLEAR_TOP 通常与 FLAG_ACTIVITY_NEW_TASK 结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent 的位置。

 

注:如果指定 Activity 的启动模式为 "standard",则该 Activity 也会从堆栈中移除,并在其位置启动一个新实例,以便处理传入的 Intent。这是因为当启动模式为 "standard" 时,将始终为新 Intent 创建新实例。

 

 

  • FLAG_ACTIVITY_CLEAR_TASK

如果在传递给Context.startActivity()的意图中设置了该标志,则会导致在启动activity 之前清除与该activity关联的任何现有任务。也就是说,activity成为一个空任务的新根,任何旧activity都finish了。

这只能与FLAG_ACTIVITY_NEW_TASK一起使用。

 

启动模式与startActivityForResult

if (sourceRecord == null) {
    // This activity is not being started from another...  in this
    // case we -always- start a new task.
    if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
        Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
              + intent);
        launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
    }
} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
    // The original activity who is starting us is running as a single
    // instance...  this new activity it is starting must go on its
    // own task.
    launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
} else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
    // The activity being started is a single instance...  it always
    // gets launched into its own task.
    launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}


if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
    // For whatever reason this activity is being launched into a new
    // task...  yet the caller has requested a result back.  Well, that
    // is pretty messed up, so instead immediately send back a cancel
    // and let the new task continue launched as normal without a
    // dependency on its originator.
    Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
    sendActivityResultLocked(-1,
            r.resultTo, r.resultWho, r.requestCode,
        Activity.RESULT_CANCELED, null);
    r.resultTo = null;
}

也就是说startActivityForResult启动的activity有FLAG_ACTIVITY_NEW_TASK,那么就不能返回结果。

onNewIntent

当通过singleTop/singleTask启动activity时,如果满足复用条件,则不会创建新的activity实例,生命周期就变为onNewIntent()---->onResart()------>onStart()----->onResume()。

    Activity第一启动的时候执行onCreate()---->onStart()---->onResume()等后续生命周期函数,也就时说第一次启动Activity并不会执行到onNewIntent().;

    而后面如果再有想启动Activity的时候,那就是执行onNewIntent()---->onResart()------>onStart()----->onResume();

    如果android系统由于内存不足把已存在Activity释放掉了,那么再次调用的时候会重新启动Activity即执行onCreate()---->onStart()---->onResume()等。

注意:当调用到onNewIntent(intent)的时候,需要在onNewIntent() 中使用setIntent(intent)赋值给Activity的Intent.否则,后续的getIntent()都是得到老的Intent。

 

清理返回栈

如果用户长时间离开一个任务,则系统会清除该任务中除了根 Activity以外的所有 Activity 。当用户再次返回到任务时,仅恢复根 Activity。系统这样做的原因是,经过很长一段时间后,用户可能已经放弃之前执行的操作,返回到任务是要开始执行新的操作。

您可以使用下列几个 Activity 属性修改此行为:

alwaysRetainTaskState

如果在任务的根 Activity 中将此属性设置为 "true",则不会发生刚才所述的默认行为。即使在很长一段时间后,任务仍将所有 Activity 保留在其堆栈中。

 

clearTaskOnLaunch

如果在任务的根 Activity 中将此属性设置为 "true",则每当用户离开任务然后返回时,系统都会将堆栈清除到只剩下根 Activity。换而言之,它与alwaysRetainTaskState 正好相反。即使只离开任务片刻时间,用户也始终会返回到任务的初始状态。

 

finishOnTaskLaunch

此属性类似于 clearTaskOnLaunch,但它对单个 Activity 起作用,而非整个任务。此外,不管是否是root Activity都有可能被销毁。设置为 "true" 时,Activity 仍是任务的一部分, 如果用户离开然后返回任务,则它不再存在。

 

 

从桌面图标和最近任务列表进入的行为不一致的问题

入口ActivityA设置了singleTask,ActivityB模式为standard,

开始点击桌面图标,ActivityA显示出来,此时在ActivityA中启动ActivityB,然后按home键返回桌面,

此时再从桌面图标进入此app,由于ActivityA是singleTask模式,所以会把之前的任务中ActivityA之上的所有Activity都finish掉,所以再从桌面图标进入此app时显示的是ActivityA。

从最近任务列表中进入的话,是ActivityB 返回到桌面之前的状态。

 

为了解决此问题,需要把ActivityA的launchMode设置为standard即可。

 

 

查看Activity的返回栈

adb shell dumpsys activity

ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)

原文链接 https://www.cnblogs.com/muouren/p/11706314.html

关注我获取更多知识或者投稿

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值