urlname
type
Post
password
SyncToConfluence
category
Android
date
May 30, 2024
slug
01HZ4P93NZHG12ZDYVFP4BVQ1Y
icon
Button
catalog
summary
tags
Android
读书笔记
cover
Status
BusyTime
Status 1
status
Published
💡
在 Android 开发中,Activity 作为四大组件之一,是开发者最常接触和使用的组件。它不仅负责界面的展示,还承担了与用户交互的主要任务。理解 Activity 的启动模式,对于开发高效、稳定的应用至关重要。本文将详细解析 Activity 的启动模式,并结合一些最新的专业知识,为大家提供一个全面的理解。

为什么需要启动模式?

我们知道,在默认情况下,当我们多次启动同一个 Activity 的时候,系统会创建多个实例并把它们一一放入任务栈中。当我们单击 Back 键,会发现这些 Activity 会一一回退。这种“后进先出”的栈结构在一定程度上确实满足了一些简单的需求,但在实际开发中,经常会遇到需要修改系统默认行为的情况。为了更好地满足不同的项目需求,Android 提供了启动模式来灵活地管理 Activity 的实例。
目前有四种启动模式:standard、singleTop、singleTask 和 singleInstance。接下来,我们一一介绍它们的含义和使用场景。

四种启动模式

standard(标准模式)

这是系统的默认模式。每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在。被创建的实例的生命周期符合典型情况下 Activity 的生命周期:onCreate -> onStart -> onResume。这种模式适合大多数普通场景,比如浏览列表,查看详情等。
注意,使用 ApplicationContext 启动标准模式的 Activity 时会报错,因为非 Activity 类型的 Context 没有任务栈。

代码示例

任务栈示例

在这种模式下,每次启动 Activity 都会创建一个新实例。例如:
任务栈中的Activity:
启动新Activity E:
再次启动Activity B:

singleTop(栈顶复用模式)

如果新启动的 Activity 已经位于任务栈的栈顶,则不会重新创建实例,而是调用其 onNewIntent 方法。否则,会重新创建实例。这种模式适用于某些不需要重复创建实例的场景,比如阅读文章时,多次点击同一篇文章。

代码示例

任务栈示例

假设任务栈中有以下Activity:
启动Activity B(不是栈顶),则新建实例:
再次启动Activity B(栈顶),调用 onNewIntent()

singleTask(栈内复用模式)

这种模式下,Activity 在任务栈中只有一个实例。当 Activity 被启动时,系统会检查该任务栈中是否已有该 Activity 的实例,如果有,则会把该实例移到栈顶,并调用 onNewIntent 方法;否则,会创建一个新的实例。适用于需要确保某个 Activity 只有一个实例的场景,比如应用的主页。

代码示例

任务栈示例

假设任务栈中的Activity为:
启动Activity E(singleTask):
启动Activity B(singleTask),则 C、D 和 E 被销毁(clearTop),B 被移到栈顶:

singleInstance(单实例模式)

Activity 独占一个新的任务栈,任何新请求都会重用这个实例。这种模式适用于需要与其他应用共享的 Activity,比如电话拨号界面、闹钟等。

代码示例

任务栈示例

假设任务栈中的活动为:
启动活动 E(singleInstance),创建新的任务栈:
再次启动 E,调用 onNewIntent(),不创建新实例。

Task Affinity 和任务栈

Task Affinity 概述

Task Affinity 标识了一个 Activity 所需的任务栈的名字。默认情况下,所有 Activity 所需的任务栈的名字为应用的包名。当然,我们可以为每个 Activity 单独指定 Task Affinity 属性,但这个属性值必须不同于包名,否则相当于没有指定。

Task Affinity 的主要使用场景

Task Affinity 属性主要在以下两种情况下有意义:
  • 与 singleTask 启动模式配对使用
  • 与 allowTaskReparenting 属性结合使用

任务栈类型

任务栈可以分为前台任务栈和后台任务栈:
  • 前台任务栈:当前用户正在与之交互的任务栈。
  • 后台任务栈:处于暂停状态的任务栈,用户可以通过切换将其调到前台。

Task Affinity 和 singleTask

当 Task Affinity 和 singleTask 启动模式配对使用时,该模式的 Activity 将运行在名字与 Task Affinity 相同的任务栈中。
为了更好地理解 Task Affinity 和 singleTask 启动模式的作用,我们将通过一个具体的例子来说明。这将涉及多个 Activity 和不同的 Task Affinity 设置,以及 singleTask 启动模式的应用。
假设我们有一个新闻应用(com.example.newsapp),它包含以下三个 Activity:
  • MainActivity:应用的主界面,显示新闻列表。
  • ArticleActivity:显示新闻文章的详细信息。
  • UserProfileActivity:显示用户个人资料。
我们希望:
  • MainActivity 在默认的任务栈中运行。
  • ArticleActivity 在单独的任务栈中运行,并且是 singleTask 模式。
  • UserProfileActivity 可以在主任务栈和文章任务栈之间切换。

XML 配置示例

以下是 AndroidManifest.xml 文件中的相关配置:

行为说明

  • MainActivity 在应用的默认任务栈中运行,没有特别的 Task Affinity 设置。
  • ArticleActivity 使用 singleTask 启动模式,并且 Task Affinity 设置为 com.example.newsapp.articleTask。这意味着:
    • 每次启动 ArticleActivity 时,如果有与 com.example.newsapp.articleTask 相同任务栈的 Activity 存在,它将重用该任务栈,并清空其上的其他 Activity。
  • UserProfileActivity 允许任务重新排列 (allowTaskReparenting="true") 并具有与 ArticleActivity 相同的 Task Affinity。这意味着:
    • 如果 UserProfileActivity 从 MainActivity 中启动,它会在主任务栈中运行。
    • 当 ArticleActivity 被启动且 UserProfileActivity 也在前台时,UserProfileActivity 将重新排列到 ArticleActivity 所在的任务栈中。

示例代码

在 MainActivity 中启动 ArticleActivity 和 UserProfileActivity:
在 ArticleActivity 中启动 UserProfileActivity:

使用流程示例

  • 启动应用,MainActivity 加载在默认任务栈中。
  • 从 MainActivity 启动 ArticleActivity,ArticleActivity 在新的任务栈中启动,任务栈的名字为 com.example.newsapp.articleTask
  • 从 ArticleActivity 启动 UserProfileActivity,UserProfileActivity 加载在 com.example.newsapp.articleTask 任务栈中。
  • 按 Back 键返回,先返回 ArticleActivity,再返回 MainActivity。
  • 从 MainActivity 启动 UserProfileActivity,UserProfileActivity 在默认任务栈中启动。
  • 从 MainActivity 再次启动 ArticleActivity,ArticleActivity 会重用 com.example.newsapp.articleTask 任务栈,并清空其上的其他 Activity,因此 UserProfileActivity 会被移除。

Task Affinity 和 allowTaskReparenting 属性

当 Task Affinity 和 allowTaskReparenting 属性结合使用时,会产生特殊效果:
  • 如果应用 A 启动了应用 B 的某个 Activity,并且该 Activity 的 allowTaskReparenting 属性为 true,那么当应用 B 被启动后,此 Activity 会从应用 A 的任务栈转移到应用 B 的任务栈中。

示例说明

假设有两个应用:应用A和应用B。应用A中有一个按钮点击事件,会启动应用B的一个 Activity。这个 Activity 的 Task Affinity 被设置为应用B的任务栈,并且 allowTaskReparenting 属性为 true。

示例场景描述

  • 应用A启动应用B的 Activity:用户在应用A中点击按钮,启动应用B中的 ActivityB。
  • 应用B启动:当用户启动应用B时,ActivityB 将从应用A的任务栈转移到应用B的任务栈。

示例代码

  • 应用A中的代码
在应用A中定义启动应用B的 Activity 的按钮点击事件:
  • 应用B中的 ActivityB 的配置
在应用B的 AndroidManifest.xml 文件中配置 ActivityB:

行为分析

  • 当用户在应用A中点击按钮时,启动了应用B的 ActivityB,此时 ActivityB 运行在应用A的任务栈中。
  • 当用户随后启动应用B时,ActivityB 会自动从应用A的任务栈转移到应用B的任务栈中。

示例详细代码

  • 应用A的 MainActivity.java
  • 应用B的 AndroidManifest.xml
  • 应用B的 ActivityB.java

用户交互流程

  • 用户打开应用A,点击按钮启动应用B的 ActivityB。
  • ActivityB 现在在应用A的任务栈中。
  • 用户启动应用B,ActivityB 自动从应用A的任务栈转移到应用B的任务栈中。

启动标志位

标志位
作用说明
FLAG_ACTIVITY_NEW_TASK
启动一个新的任务栈。如果目标 Activity 已存在于另一个任务栈中,则该 Activity 及其上方的所有 Activity 会移到新的任务栈中。
FLAG_ACTIVITY_SINGLE_TOP
如果目标 Activity 已位于任务栈的顶部,则复用该实例,并通过 onNewIntent 方法传递新的 Intent,否则创建新实例。
FLAG_ACTIVITY_CLEAR_TOP
如果目标 Activity 已存在于任务栈中,则弹出其上的所有 Activity,并通过 onNewIntent 方法传递新的 Intent。
FLAG_ACTIVITY_NO_HISTORY
启动的 Activity 不会保存在任务栈中,用户离开该 Activity 后即销毁。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
使启动的 Activity 不出现在“最近任务”列表中。
FLAG_ACTIVITY_CLEAR_TASK
在启动新 Activity 前,清除其所在任务栈中的所有 Activity,并结合 FLAG_ACTIVITY_NEW_TASK 使用。
FLAG_ACTIVITY_REORDER_TO_FRONT
将已存在任务栈中的目标 Activity 移动到前台,而不重新创建实例。
 
Activity-生命周期与IntentFilter深度学习-常见神经网络层
Loading...