FLAG_ACTIVITY_RESET_TASK_IF_NEEDED和FLAG_ACTIVITY_CLEAR_TOP有什么区别? FLAG_ACTIVITY_SINGLE_TOP?

What Are the Differences Between FLAG_ACTIVITY_RESET_TASK_IF_NEEDED and FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP?

我正在(终于)为我的书写关于任务的章节,我遇到了一些挥之不去的困惑。

用作主屏幕启动器的东西在启动请求的启动器时似乎使用 FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_RESET_TASK_IF_NEEDED 的组合 activity:

Intent i=new Intent(Intent.ACTION_MAIN);

i.addCategory(Intent.CATEGORY_LAUNCHER);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
i.setComponent(name);

startActivity(i);  

The documentation for FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 有:

If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed.

不是特别清楚。

特别是,使用 FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_SINGLE_TOP 的组合似乎可以看到相同的效果。引用 FLAG_ACTIVITY_CLEAR_TOP:

的文档

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent...

The currently running instance of [the desired activity] will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent. If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent().

FLAG_ACTIVITY_CLEAR_TOP 文档很有意义,至少对我来说是这样。

那么,FLAG_ACTIVITY_RESET_TASK_IF_NEEDEDFLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_SINGLE_TOP 的组合有什么不同?


如果您能解释 FLAG_ACTIVITY_CLEAR_TASK 与上述其他两个选项的不同之处,可加分。

If set in an Intent passed to Context.startActivity(), this flag will cause any existing task that would be associated with the activity to be cleared before the activity is started. That is, the activity becomes the new root of an otherwise empty task, and any old activities are finished. This can only be used in conjunction with FLAG_ACTIVITY_NEW_TASK.

这和FLAG_ACTIVITY_CLEAR_TOP之间的一个明显区别 | FLAG_ACTIVITY_SINGLE_TOPFLAG_ACTIVITY_CLEAR_TASK需要FLAG_ACTIVITY_NEW_TASK。但是,除此之外,看起来净效果是一样的,并且也匹配 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED.

我在这里可能弄错了,但据我所知,FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 确实分析了 所有 任务,并确保只有一个任务 activity 是 运行。

FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP 将仅检查当前任务,因此您最终可能会同时拥有 2 个启动器实例 运行(如果第一个是作为单独的任务创建的)。

1) FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

如果某些任务挂起,它将破坏该进程并启动您请求的activity

2) FLAG_ACTIVITY_CLEAR_TOP

如果此 activity 的任何先前意图是 运行,则此方法将交付 activity 的 运行 实例,关闭所有其他活动并将启动activity 与之前的实例。

3) FLAG_ACTIVITY_SINGLE_TOP

如果最近启动了这个 activity,并且保存了实例,那么它将不会启动这个 activity。

我查看了 ActivityManager 的源代码。标志 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 确实做了一些 Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP 做不到的事情:它触发了 任务重新设置 .

这是一个(虽然很蹩脚的)例子:

在应用程序 A 中,我们有根 Activity RootA 我们还有另一个 Activity ReparentableA:

<application
        android:label="@string/app_name">
    <activity android:name=".RootA">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <activity android:name=".ReparentableA"
            android:allowTaskReparenting="true"/>
</application>

应用程序 A 的包名称为 "com.app.a",因此其组件的默认 taskAffinity 为 "com.app.a"。

在应用程序 B 中,我们有根 Activity RootB:

<application
        android:label="@string/app_name">
    <activity android:name="RootB">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
</application>

应用程序 B 的包名称为 "com.app.b",因此其组件的默认 taskAffinity 为 "com.app.b"。

现在我们从主页屏幕启动应用程序 B。这将启动一个新任务并创建一个 Activity RootB 的新实例作为该任务中的根 Activity。 Activity RootB 现在以标准方式启动 Activity ReparentableA,没有任何特殊标志。 ReparentableA 的一个实例被创建并放在当前任务中的 RootB 之上。

按主页。

现在我们从主页屏幕启动应用程序 A。这将启动一个新任务并创建一个 Activity RootA 的新实例作为该任务中的根 Activity。注意:当 Android 启动 "launcher" Intent 时,它会自动设置标志 Intent.FLAG_ACTIVITY_NEW_TASKIntent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED。因此,启动 RootA 现在会触发任务重新设置父级。 Android 查看是否有任何其他任务中的任何活动与此新任务有亲和力(并且是可重定的)。它在 App B 任务中找到 ReparentableA(与 RootA 具有相同的任务关联)并将其移动到新的 App A 任务。启动应用程序 A 时,我们看不到 RootA,我们实际上看到 ReparentableA,因为它已移至新任务的顶部。

如果我们 return 应用程序 B,我们可以看到 ReparentableA 从任务堆栈中消失了,该任务现在只包含一个 Activity:RootB .


使用注意事项Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP

关于将这些标志用于 "reset a task" 的重要一点是,它仅在任务的根目录中已经存在目标 Activity 的实例时才有效。如果您的根 Activity 曾经完成,您无法通过使用 Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP 启动根 Activity 来清除您的任务。 Android 将只创建目标(root)的新实例 Activity 并将其 放在任务 中的现有活动之上,这可能不在应有尽有


Intent.FLAG_ACTIVITY_CLEAR_TASKIntent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP的区别:

如上所述,只有在任务中已经存在目标 Activity 的实例时,使用 CLEAR_TOP | SINGLE_TOP 才有效。然而,CLEAR_TASK 从任务中删除所有活动,无论任务中是否存在目标 Activity 的实例。此外,使用 CLEAR_TASK 可确保目标 Activity 成为任务的根 Activity,而无需知道 Activity 是根 Activity 之前清除了任务。


Intent.FLAG_ACTIVITY_CLEAR_TASKIntent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的区别:

如上所述,使用 CLEAR_TASK始终 从任务中删除所有活动并启动目标 activity 的新实例。相反,RESET_TASK_IF_NEEDED 只会在某些情况下重置任务("IF_NEEDED" 部分)。任务只有 "reset" 如果 Android 是:

  • 创建一个新任务(在这种情况下 "reset" 功能涉及上面解释的任务重命名),或者
  • 如果 Android 将后台任务带到前台(在这种情况下,任务仅清除使用 Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET 启动的任何活动以及在这些活动之上的任何活动).注意:在这种情况下,根 Activity 永远不会被清除。

重要提示: 测试时,请注意 Android 从主屏幕(或从可用应用程序列表)以及从最近的任务列表中选择任务时。

在第一种情况下(通过从可用应用程序列表或主屏幕上的快捷方式中选择应用程序来启动它),会创建一个带有 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 的启动器 Intent。无论该应用程序是否已经 运行,都会使用它。 Intent 启动,然后 ActivityManager 确定要做什么。

第二种情况(从最近任务列表中选择一个任务),如果该任务仍然存在,则将其置于最前面。如果使用最近的任务列表将任务放在最前面,则不会执行任务 "reset"。我不清楚这是如何管理的,我也没有机会查看源代码来弄清楚为什么会这样。


我希望这能回答您的问题。期待您的反馈和测试结果。