“最近使用的应用”屏幕(也称为概览屏幕、近期任务列表或最近用过的应用屏幕)是一个系统级界面,上面列出了最近访问过的activity和任务。用户可以浏览该列表,选择要恢复的任务,或通过滑动来从列表中移除任务。
“最近使用的应用”屏幕使用以文档为中心的模型(在 Android 5.0 [API 级别 21] 中引入),也就是说,如果同一 Activity 的多个实例包含不同的文档,它们可能会在“最近使用的应用”屏幕中显示为任务。比如说,Google 云端硬盘的几个 Google 文档可能各自都有一个任务。每个文档都会在“最近使用的应用”屏幕中显示为任务:
“最近使用的应用”屏幕上显示两个 Google 云端硬盘文档,每个文档都表示为单独的任务。
另一个常见的例子是当用户使用浏览器时,点按分享 > Gmail 。系统会显示 Gmail 应用的写邮件 屏幕。此时点按“最近使用的应用”按钮会显示 Chrome 和 Gmail 作为单独的任务运行:
“最近使用的应用”屏幕上显示 Chrome 和 Gmail
作为单独的任务运行。
通常情况下,您应允许系统定义您的任务和 activity 在“最近使用的应用”屏幕中的表示方式。您无需修改此行为。不过,您的应用可以确定 activity 在“最近使用的应用”屏幕中的显示方式和时间。
ActivityManager.AppTask 类可让您管理任务,而 Intent 类的 activity 标记可让您指定何时在“最近使用的应用”屏幕中添加或移除 activity。此外,您还可以使用
行为。
注意 :本指南介绍了操作系统级任务管理以及您的应用在
系统的“最近使用的应用”屏幕中的显示方式。如需管理 Compose 应用中的界面导航,
请参阅 导航 3。
将任务添加到“最近使用的应用”屏幕
使用 Intent 类的标记添加任务,您可以更好地控制在“最近使用的应用”屏幕中打开或重新打开文档的方式和时间。当您使用
选择始终在新任务中打开文档或重复使用文档的现有
任务。
使用 Intent 标记添加任务
为 activity 创建新文档时,您可以调用
startActivity()
方法并向其传递到启动该 activity 的 intent。要插入逻辑
断点,以便系统将您的 activity 视为“最近使用的应用”
屏幕中的新任务,请将
FLAG_ACTIVITY_NEW_DOCUMENT
标记传入启动该
activity 的 Intent 的 addFlags()
方法。
注意: FLAG_ACTIVITY_NEW_DOCUMENT
标记取代了
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
标记,后者在 Android 5.0(API 级别 21)中已弃用。
如果您在创建新文档时设置了 FLAG_ACTIVITY_MULTIPLE_TASK
标记,则系统始终会以
目标 activity 为根来创建新任务。此设置支持同一文档在多个任务中打开。以下代码演示了主 activity 如何执行此操作并从可组合项启动新 activity:
private fun newDocumentIntent(context: Context): Intent =
Intent(context, NewDocumentActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)
putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, documentCounter++)
}
@Composable
fun CreateDocumentButton() {
val context = LocalContext.current
Button(
onClick = {
val intent = newDocumentIntent(context)
// Add FLAG_ACTIVITY_MULTIPLE_TASK if needed based on state
context.startActivity(intent)
}
) {
Text("Create New Document")
}
}
注意: 使用 FLAG_ACTIVITY_NEW_DOCUMENT 标记启动的 activity 必须在
清单中设置 android:launchMode="standard" 属性值(默认值)。
当主 activity 启动一个新 activity 时,系统会在现有任务中进行搜索,看是否任务的 intent 与该 activity 的 intent 组件名称和 intent 数据匹配。如果未找到该任务,或 intent 包含 FLAG_ACTIVITY_MULTIPLE_TASK 标记,那么将以该 activity 为根创建一个新任务。
如果系统找到一个任务,其 intent 与 intent 组件名称和
intent 数据匹配,则会将其带到最前端,并将新 intent 传递到
onNewIntent()。
新 activity 获取 intent,并在“最近使用的应用”屏幕中创建新文档,如下例所示:
class DocumentCentricActivity : ComponentActivity() {
private var documentState by mutableStateOf(
DocumentState(
count = 0,
textResId = R.string.hello_new_document_counter
)
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val initialCount = intent.getIntExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0)
documentState = documentState.copy(count = initialCount)
setContent {
MaterialTheme {
DocumentScreen(
count = documentState.count,
textResId = documentState.textResId
)
}
}
}
override fun onNewIntent(newIntent: Intent) {
super.onNewIntent(newIntent)
// If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this Activity is reused.
documentState = documentState.copy(
textResId = R.string.reusing_document_counter
)
}
data class DocumentState(val count: Int, @StringRes val textResId: Int)
companion object {
const val KEY_EXTRA_NEW_DOCUMENT_COUNTER = "KEY_EXTRA_NEW_DOCUMENT_COUNTER"
}
}
@Composable
fun DocumentScreen(count: Int, @StringRes textResId: Int) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center
) {
// UI reacts to whichever string resource ID was passed down
Text(text = stringResource(id = textResId))
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Counter: $count")
}
}
在上面的代码中,activity 处理操作系统级路由(onCreate 和 onNewIntent),而 @Composable 函数仅负责根据提供的状态呈现界面。
使用 activity 属性添加任务
activity 还可以在其清单中使用
任务。该属性具有四个值,在用户使用应用打开文档时分别会产生以下效果:
intoExisting
activity 重复使用文档的现有任务。这与设置了
FLAG_ACTIVITY_NEW_DOCUMENT
标记但没有设置
FLAG_ACTIVITY_MULTIPLE_TASK
标记的效果相同,如
使用 Intent 标记添加任务部分所述。
always
activity 会为文档创建新任务,即使文档已打开也一样。使用该值与同时设置了 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 标记的效果相同。
none
activity 不会为文档创建新任务。“最近使用的应用”屏幕会以默认方式处理 activity。它会显示应用的单个任务,该任务是从用户上次调用的任何 activity 恢复的。
never
activity 不会为文档创建新任务。设置此值会替换 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 标记的行为。如果在 intent 中设置了任意一个,而且“最近使用的应用”屏幕会显示应用的单个任务,该任务是从用户上次调用的任何 activity 恢复的。
注意: 如果值不是 none 和 never,必须使用
launchMode="standard" 定义 activity。如果未指定此属性,系统会使用
documentLaunchMode="none"。
移除任务
默认情况下,当相关的 activity 完成时,文档任务会自动从“最近使用的应用”屏幕中退出。您可以使用
ActivityManager.AppTask
类、Intent标记或
将
android:excludeFromRecents
设置为 true,即可始终将任务从“最近使用的应用”屏幕中完全排除。
将
android:maxRecents 设置为一个
整数,即可设置您的应用可在
“最近使用的应用”屏幕中包含的最大任务数。一旦达到最大任务数,最早使用的任务将从“最近使用的应用”屏幕中消失。默认值为 16,最大值为 50(内存较低的设备上为 25)。小于 1 的值无效。
使用 AppTask 类移除任务
对于在“最近使用的应用”屏幕中创建新任务的 activity,您可以通过调用
finishAndRemoveTask()
方法来指定何时应移除任务并完成与该任务相关联的所有 activity:
@Composable
fun RemoveTaskButton() {
val context = LocalContext.current
Button(
onClick = {
// It is good practice to remove a document from the overview stack if not needed anymore.
(context as? Activity)?.finishAndRemoveTask()
}
) {
Text("Remove from Recents")
}
}
注意: 使用
finishAndRemoveTask()
方法会替换下一部分中讨论的
FLAG_ACTIVITY_RETAIN_IN_RECENTS
标记的使用。
保留已完成的任务
如果您即使在任务的 activity 已完成后也希望在“最近使用的应用”屏幕中保留它,请将
FLAG_ACTIVITY_RETAIN_IN_RECENTS
标记传入启动该 activity 的
intent 的
addFlags() 方法。
private fun newDocumentIntent() =
Intent(this, NewDocumentActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)
putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, getAndIncrement())
}
要达到同样的效果,请将
android:autoRemoveFromRecents
设置为 false。文档 activity 的默认值为 true,常规 activity 的默认值为 false。使用此属性会替换 FLAG_ACTIVITY_RETAIN_IN_RECENTS 标记。
启用“最近使用的应用”网址共享(仅限 Pixel)
在搭载 Android 12 或更高版本的 Pixel 设备上,用户可以直接通过“最近使用的应用”屏幕分享最近浏览的网页内容的链接。在某个应用中访问内容后,用户可以滑动到“最近使用的应用”屏幕,并找到在其中查看了相应内容的应用,然后点按链接按钮来复制或分享网址。
“最近使用的应用”屏幕,其中包含用于分享
最近浏览的 Web 内容的链接。
任何应用都可以通过提供 Web 界面并
替换
onProvideAssistContent()来为用户启用“最近使用的应用”链接,
如以下示例所示:
class MainActivity : ComponentActivity() {
// Track the current URL as state so the UI can update it during navigation
private var currentWebUri by mutableStateOf("https://example.com/home")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
// Pass a lambda to your Compose UI so it can update the URL state
// as the user navigates through your app.
MainScreen(
onPageChanged = { newUrl -> currentWebUri = newUrl }
)
}
}
}
override fun onProvideAssistContent(outContent: AssistContent) {
super.onProvideAssistContent(outContent)
// The system calls this when the user enters the Recents screen.
// Provide the active URI tracked by the Compose state.
outContent.webUri = Uri.parse(currentWebUri)
}
}