“最近使用的应用”屏幕(也称为概览屏幕、近期任务列表或最近用过的应用屏幕)是一个系统级界面,上面列出了最近访问过的activity和任务。用户可以浏览该列表,选择要恢复的任务,或通过滑动来从列表中移除任务。
“最近使用的应用”屏幕使用以文档为中心的模型(在 Android 5.0 [API 级别 21] 中引入),也就是说,如果同一 Activity 的多个实例包含不同的文档,它们可能会在“最近使用的应用”屏幕中显示为任务。比如说,Google 云端硬盘的几个 Google 文档可能各自都有一个任务。每个文档都会在“最近使用的应用”屏幕中显示为任务:
另一个常见的例子是当用户使用浏览器时,点按分享 > Gmail 。系统会显示 Gmail 应用的写邮件 屏幕。此时点按“最近使用的应用”按钮会显示 Chrome 和 Gmail 作为单独的任务运行:
通常情况下,您应允许系统定义您的任务和 activity 在“最近使用的应用”屏幕中的表示方式。您无需修改此行为。不过,您的应用可以确定 activity 在“最近使用的应用”屏幕中的显示方式和时间。
ActivityManager.AppTask 类可让您管理任务,而 Intent 类的 activity 标记可让您指定何时在“最近使用的应用”屏幕中添加或移除 activity。此外,您还可以使用
<activity>属性在清单中设置
行为。
将任务添加到“最近使用的应用”屏幕
使用 Intent 类的标记添加任务,您可以更好地控制在“最近使用的应用”屏幕中打开或重新打开文档的方式和时间。当您使用
<activity>属性时,您可以
选择始终在新任务中打开文档或重复使用文档的现有
任务。
使用 Intent 标记添加任务
为 activity 创建新文档时,您可以调用
startActivity()
方法并向其传递到启动该 activity 的 intent。要插入逻辑
断点,以便系统将您的 activity 视为“最近使用的应用”
屏幕中的新任务,请将
FLAG_ACTIVITY_NEW_DOCUMENT
标记传入启动该
activity 的 Intent 的 addFlags()
方法。
如果您在创建新文档时设置了 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") } }
当主 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 还可以在其清单中使用 <activity>属性 android:documentLaunchMode指定始终启动到新
任务。该属性具有四个值,在用户使用应用打开文档时分别会产生以下效果:
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 恢复的。
移除任务
默认情况下,当相关的 activity 完成时,文档任务会自动从“最近使用的应用”屏幕中退出。您可以使用
ActivityManager.AppTask
类、Intent标记或
<activity>属性来替换此行为。
将
<activity>属性
android:excludeFromRecents
设置为 true,即可始终将任务从“最近使用的应用”屏幕中完全排除。
将
<activity> 属性
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") } }
保留已完成的任务
如果您即使在任务的 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()) }
要达到同样的效果,请将
<activity>属性
android:autoRemoveFromRecents
设置为 false。文档 activity 的默认值为 true,常规 activity 的默认值为 false。使用此属性会替换 FLAG_ACTIVITY_RETAIN_IN_RECENTS 标记。
启用“最近使用的应用”网址共享(仅限 Pixel)
在搭载 Android 12 或更高版本的 Pixel 设备上,用户可以直接通过“最近使用的应用”屏幕分享最近浏览的网页内容的链接。在某个应用中访问内容后,用户可以滑动到“最近使用的应用”屏幕,并找到在其中查看了相应内容的应用,然后点按链接按钮来复制或分享网址。
任何应用都可以通过提供 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) } }