导航和返回堆栈

NavController 包含“返回堆栈”,该堆栈包含用户访问过的目的地。当用户在应用中导航到屏幕时,NavController 会在返回堆栈中添加和移除目的地。

作为堆栈,返回堆栈采用“后进先出”的数据结构。因此,NavController 会将项推送到堆栈并从堆栈顶部弹出项。

基本行为

关于返回堆栈的行为,您应考虑以下核心事实:

  • 第一个目的地:当用户打开应用时,NavController 会将第一个目的地推送到返回堆栈的顶部。
  • 推送到堆栈:每次调用 NavController.navigate() 都会将给定目的地推送到堆栈顶部。
  • 弹出顶部目的地:点按向上返回会分别调用 NavController.navigateUp()NavController.popBackStack() 方法。它们会将弹出目的地从堆栈中弹出。如需详细了解向上返回之间的区别,请参阅导航原则页面。

返回

NavController.popBackStack() 方法会尝试将当前目的地从返回堆栈中弹出,并导航到上一个目的地。这样可以有效地将用户在其导航历史记录中向后移动一步。该方法会返回一个布尔值,表明它是否已成功返回到目的地。

返回到特定目的地

您还可以使用 popBackStack() 导航到特定目的地。为此,请使用其过载之一。有多个方法允许您传入标识符,例如整数 id 或字符串 route。这些过载会将用户转到与指定标识符相关联的目的地。重要的是,它们会弹出该目的地上方堆栈中的所有内容。

这些过载还采用 inclusive 布尔值。它可确定 NavController 在转到指定目的地后,是否也应从返回堆栈中弹出该目的地。

以下面这个简短的代码段为例:

navController.popBackStack(R.id.destinationId, true)

在这里,NavController 会返回到具有整数 ID destinationId 的目的地。由于 inclusive 参数的值为 true,因此 NavController 还会从返回堆栈中弹出给定目的地。

处理失败的返回

popBackStack() 返回 false 时,对 NavController.getCurrentDestination() 的后续调用会返回 null。这意味着应用已从返回堆栈中弹出最后一个目的地。在这种情况下,用户只会看到一个空白屏幕。

这可能发生在以下情况:

  • popBackStack() 没有从堆栈中弹出任何内容。
  • popBackStack() 确实从返回堆栈中弹出了目的地,但堆栈现在为空。

如需解决此问题,您随后必须导航到新目的地或对活动调用 finish() 以结束它。以下代码段演示了此过程:

Kotlin

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

java

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

弹出到目的地

如需在从一个目的地导航到另一个目的地时从返回堆栈中移除目的地,请将 popUpTo() 参数添加到关联的 navigate() 函数调用中。popUpTo() 会指示 Navigation 库在调用 navigate() 的过程中从返回堆栈中移除一些目的地。参数值是返回堆栈上目的地的标识符。标识符可以是整数 id 或字符串 route

您可以为 inclusive 参数添加一个值为 true 的参数,以指明您在 popUpTo() 中指定的目的地也应从返回堆栈中弹出。

如需以程序化方式实现此方法,请将 popUpTo() 作为 NavOptions 的一部分传递给 navigate(),同时将 inclusive 设置为 true。这适用于 Compose 和 View。

弹出时保存状态

使用 popUpTo 导航到某个目的地时,您可以选择保存从返回堆栈中弹出的所有目的地的状态。

如需启用此选项,请在关联的 action 中将 popUpToSaveState 定义为 true,或调用 NavController.navigate()

当您导航到目的地时,您还可以将 restoreSaveState 定义为 true,以自动恢复与 destination 属性中的目的地相关联的状态。

XML 示例

下面是一个在 XML 中使用操作的 popUpTo 示例:

<action
  android:id="@+id/action_a_to_b"
  app:destination="@id/b"
  app:popUpTo="@+id/a"
  app:popUpToInclusive="true"
  app:restoreState=”true”
  app:popUpToSaveState="true"/>

Compose 示例

下面是 Compose 中相同功能的完整示例:

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: String = "destination_a"
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable("destination_a") {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to the "destination_a" destination off the back stack before
                // navigating to the "destination_b" destination
                    navController.navigate("destination_b") {
                        popUpTo("destination_a") {
                            inclusive = true
                            saveState = true
                        }
                    }
                },
            )
        }
        composable("destination_b") { DestinationB(/* ... */) }
    }
}

@ Composable
fun DestinationA(onNavigateToB: () -> Unit) {
    Button(onClick = onNavigateToB) {
        Text("Go to A")
    }
}

您可以通过以下方式更精细地更改调用 NavController.navigate() 的方式:

// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a")
}

// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a") { inclusive = true }
}

// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
    launchSingleTop = true
}

如需了解有关将选项传递给 NavController.navigate() 的常规信息,请参阅“使用选项进行导航”指南

使用操作实现弹出

使用操作进行导航时,您可以选择从返回堆栈上弹出其他目的地。例如,如果您的应用具有初始登录流程,那么在用户登录后,您应将所有与登录相关的目的地从返回堆栈上弹出,这样返回按钮就不会将用户带回登录流程。

深入阅读

如需了解详情,请参阅以下页面:

  • 循环导航:了解在导航流程为循环的情况下如何避免过度填充的返回堆栈。
  • 对话框目的地:了解对话框目的地如何带来管理返回堆栈方面的一些特殊注意事项。