Activity 类是 Android 应用的一个关键组件,而 Activity 的启动和组合方式是该平台应用模型的基础。与那些通过 main 方法启动应用的编程范式不同,Android 系统通过调用与 Activity 实例生命周期特定阶段相对应的回调方法来启动代码。

本文档介绍了 Activity 的概念,并提供了关于如何使用 Activity 的简要指南。有关应用架构最佳实践的更多信息,请参阅应用架构指南。

Activity 的概念

移动应用体验与桌面应用不同,用户与应用的交互并不总是从同一个地方开始。相反,用户旅程通常以非确定性的方式开始。例如,如果您从主屏幕打开电子邮件应用,您可能会看到电子邮件列表。相比之下,如果您正在使用社交媒体应用,然后通过它启动了电子邮件应用,您可能会直接进入该电子邮件应用的撰写界面。

Activity 类旨在促进这种范式。当一个应用调用另一个应用时,调用方应用会启动另一个应用中的 Activity,而不是将该应用视为一个原子整体。通过这种方式,Activity 成为了应用与用户交互的入口点。您可以将 Activity 实现为 Activity 类的子类。

Activity 提供了一个窗口,应用可以在其中绘制 UI。此窗口通常会填满屏幕,但也可能小于屏幕并浮动在其他窗口之上。

通常,应用中的一个 Activity 被指定为“主 Activity”(main activity),这是用户启动应用时首先出现的屏幕。在现代 Compose 应用中,这是唯一需要的 Activity,因为它在单 Activity 架构中托管可组合项(composables),而不是拥有视图层次结构。应用不再为不同的屏幕设置多个 Activity,而是由 Activity 中的可组合项托管多个导航目的地。

要在应用中使用 Activity,必须在应用的清单(manifest)中注册相关信息,并且了解 Activity 的生命周期也是一种良好的做法。本文档的其余部分将介绍这些主题。

配置清单

为了使应用能够使用 Activity,您必须在清单中声明这些 Activity 及其某些属性。

声明 Activity

要声明您的 Activity,请打开清单文件并添加一个 元素作为 元素的子元素。例如:

...

...

此元素唯一必需的属性是 android:name,它指定了 Activity 的类名。您还可以添加定义 Activity 特性的属性,例如标签、图标或 UI 主题。有关这些属性及其他属性的更多信息,请参阅 元素参考文档。

注意: 发布应用后,不应更改 Activity 名称。如果更改,可能会破坏某些功能,例如应用快捷方式。有关发布后应避免的更改的更多信息,请参阅无法更改的内容。

声明 Intent 过滤器

Intent 过滤器(Intent filters)是 Android 平台的一项非常强大的功能。它们不仅能够根据“显式”请求启动 Activity,还能够根据“隐式”请求启动 Activity。例如,显式请求可能会指示系统“启动 Gmail 应用中的发送电子邮件 Activity”。相比之下,隐式请求则指示系统“启动任何能完成该工作的 Activity 中的发送电子邮件屏幕”。当系统 UI 询问用户使用哪个应用来执行任务时,这就是 Intent 过滤器在起作用。

您可以通过在 元素中声明 属性来利用此功能。此元素的定义包含一个 元素,以及可选的 元素和/或 元素。这些元素组合在一起,指定了您的 Activity 可以响应的 Intent 类型。例如,以下代码片段展示了如何配置一个发送文本数据和电子邮件的 Activity,并接收来自其他 Activity 的相关请求:

在此示例中, 元素指定此 Activity 用于发送数据。将 元素声明为 DEFAULT,使 Activity 能够接收启动请求。 元素指定了此 Activity 可以发送的数据类型。以下代码片段展示了如何调用上述 Activity 来撰写电子邮件:

fun composeEmail(addresses: Array, subject: String) {

val intent = Intent(Intent.ACTION_SENDTO).apply {

data = Uri.parse("mailto:") // Only email apps handle this.

putExtra(Intent.EXTRA_EMAIL, addresses)

putExtra(Intent.EXTRA_SUBJECT, subject)

}

if (intent.resolveActivity(packageManager) != null) {

startActivity(intent)

}

}

如果您希望应用是自包含的,不允许其他应用启动其 Activity,则无需任何其他 Intent 过滤器。不希望向其他应用公开的 Activity 不应包含任何 Intent 过滤器,您可以使用显式 Intent 自行启动它们。有关您的 Activity 如何响应 Intent 的更多信息,请参阅Intent 和 Intent 过滤器。

处理传入的 Intent

以下示例展示了一种在处理多种 Intent 类型(单个文本分享、单个图像和多个图像数组)时管理 Activity 生命周期的模式。通过将这些不同的输入路由到一个集中的 handleIntent 函数中,可以确保 ACTION_SEND 和 ACTION_SEND_MULTIPLE 操作被正确解析并委派给 ViewModel,从而实现响应式 UI 更新。

class ExampleActivity : ComponentActivity() {

private val viewModel: MyViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

handleIntent(intent)

setContent {

ComposeApp(viewModel)

}

}

override fun onNewIntent(intent: Intent) {

super.onNewIntent(intent)

setIntent(intent)

handleIntent(intent)

}

private fun handleIntent(intent: Intent?) {

when (intent?.action) {

Intent.ACTION_SEND -> {

if ("text/plain" == intent.type) {

intent.getStringExtra(Intent.EXTRA_TEXT)?.let {

viewModel.handleText(it) // Update UI to reflect text being shared

}

} else if (intent.type?.startsWith("image/") == true) {

(intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))?.let {

viewModel.handleImage(it) // Update UI to reflect image being shared

}

}

}

Intent.ACTION_SEND_MULTIPLE -> {

if (intent.type?.startsWith("image/") == true) {

intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)?.let {

viewModel.handleMultipleImages(it) // Update UI to reflect multiple images being shared

}

} else {

// Handle other types

}

}

else -> {

// Handle other intents

}

}

}

}

声明权限

您可以使用清单中的 标签来控制哪些应用可以启动特定的 Activity。除非父 Activity 和子 Activity 在其清单中具有相同的权限,否则父 Activity 无法启动子 Activity。如果您为父 Activity 声明了 元素,则每个子 Activity 必须具有匹配的 元素。

例如,如果您的应用想要使用名为 SocialApp 的假设应用在社交媒体上分享帖子,SocialApp 本身必须定义调用它的应用所必须具备的权限:

android:permission="com.google.socialapp.permission.SHARE_POST"

/>

然后,为了获得调用 SocialApp 的许可,您的应用必须与 SocialApp 清单中设置的权限相匹配:

有关权限和常规安全性的更多信息,请参阅安全检查清单。

管理 Activity 生命周期

在整个生命周期中,Activity 会经历多种状态。您可以使用一系列回调来处理状态之间的转换。以下各节介绍了这些回调。在 Compose 应用中,不建议直接挂钩这些回调。相反,请使用 Lifecycle API 来观察状态变化。有关更多信息,请参阅将生命周期与 Compose 集成。

onCreate

您必须实现此回调,它会在系统创建您的 Activity 时触发。您的实现应在此初始化 Activity 的基本组件:例如,应用应在此创建视图并将数据绑定到列表。

在 Compose 应用中,使用此回调通过 setContent 设置您的宿主可组合项,如下所示:

class MyActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContent {

Text(text = stringResource(id = R.string.greeting))

}

}

}

当 onCreate 完成时,下一个回调始终是 onStart。

onStart

当 onCreate 退出时,Activity 进入 Started 状态,并且对用户可见。此回调包含 Activity 为进入前台并变得可交互而做的最后准备工作。

onResume

系统会在 Activity 开始与用户交互之前调用此回调。此时,Activity 位于 Activity 栈的顶部,并捕获所有用户输入。应用的大多数核心功能都是在 onResume 方法中实现的。

onPause 回调始终紧跟在 onResume 之后。

onPause

当 Activity 失去焦点并进入 Paused(已暂停)状态时,系统会调用 onPause。当用户点击“返回”或“最近使用”按钮时,就会发生这种情况。当系统为您的 Activity 调用 onPause 时,从技术上讲,这意味着您的 Activity 仍然部分可见,但通常表示用户正在离开该 Activity,并且该 Activity 即将进入 Stopped 或 Resumed 状态。

如果用户期望 UI 更新,处于 Paused 状态的 Activity 可以继续更新 UI。此类 Activity 的示例包括显示导航地图屏幕或正在播放的媒体播放器。即使此类 Activity 失去焦点,用户也期望其 UI 继续更新。

您不应使用 onPause 来保存应用或用户数据、执行网络调用或执行数据库事务。有关保存数据的详细信息,请参阅保存和恢复临时 UI 状态。

一旦 onPause 执行完毕,下一个回调要么是 onStop,要么是 onResume,具体取决于 Activity 进入 Paused 状态后发生的情况。

onStop

当 Activity 对用户不再可见时,系统会调用 onStop。这可能是因为 Activity 即将被销毁、新的 Activity 正在启动,或者现有的 Activity 进入 Resumed 状态并覆盖了已停止的 Activity。在所有这些情况下,已停止的 Activity 都已不再可见。

系统调用的下一个回调要么是 onRestart(如果 Activity 即将恢复与用户交互),要么是 onDestroy(如果此 Activity 将彻底终止)。

onRestart

当处于 Stopped 状态的 Activity 即将重新启动时,系统会调用此回调。onRestart 会将 Activity 从停止时的时间点恢复其状态。

此回调之后始终是 onStart。

onDestroy

在 Activity 被销毁之前,系统会调用此回调。

这是 Activity 接收到的最后一个回调。onDestroy 通常用于确保在 Activity 或包含它的进程被销毁时,释放 Activity 的所有资源。

本节仅对该主题进行了介绍。有关 Activity 生命周期及其回调的更详细处理,请参阅Activity 生命周期。