保存和管理导航状态

以下部分介绍了保存返回堆栈以及存储与返回堆栈中的条目关联的状态的策略。

保存返回堆栈

确保应用的导航状态在各种生命周期事件(包括配置更改和进程终止)中保持不变,对于提供良好的用户体验至关重要。在 Navigation 3 中,您拥有自己的返回堆栈,因此没有关于如何创建或保存返回堆栈的严格准则。不过,Navigation 3 确实提供了一种便捷方法,可为您提供可保存的返回堆栈:rememberNavBackStack。

使用 rememberNavBackStack

rememberNavBackStack 可组合函数旨在创建一个在配置更改和进程终止后仍能保留的返回堆栈。

为确保 rememberNavBackStack 正常运行,返回堆栈中的每个键都必须符合特定要求:

  • 实现 NavKey 接口:返回堆栈中的每个键都必须实现 NavKey 接口。这会充当标记接口,向库发出信号,表明可以保存密钥。
  • 具有 @Serializable 注解:除了实现 NavKey 之外,您的键类和对象还必须使用 @Serializable 注解进行标记。

以下代码段展示了 rememberNavBackStack 的正确实现:

@Serializable
data object Home : NavKey

@Composable
fun NavBackStack() {
    val backStack = rememberNavBackStack(Home)
}

记住包含 NavKey 子类型的返回堆栈

rememberNavBackStack 可组合函数会返回一个 NavBackStack<NavKey>。 如果您的应用定义了自己的 NavKey 子类型,并且其所有键都继承自该子类型,您可以通过实现自定义 remember 函数来保留该类型,如下所示:

@Serializable
sealed interface MyAppNavKey : NavKey

@Serializable
data object ScreenA: MyAppNavKey

@Serializable
data class ScreenB(val id: String): MyAppNavKey

@Composable
fun rememberMyAppNavBackStack(vararg elements: MyAppNavKey): NavBackStack<MyAppNavKey> {
    return rememberSerializable(serializer = serializer()) {
        NavBackStack(*elements)
    }
}

@Composable
fun MyApp() {
    // defaultNavBackStack is NavBackStack<NavKey>
    val defaultNavBackStack = rememberNavBackStack(ScreenA)
    // myAppNavBackStack is NavBackStack<MyAppNavKey>
    val myAppNavBackStack = rememberMyAppNavBackStack(ScreenA)
}

如需查看更多示例(包括如何处理开放多态性),请参阅 NavBackStackSamples。

替代方案:存储在 ViewModel 中

管理返回堆栈的另一种方法是将其存储在 ViewModel 中。 如需在使用 ViewModel 或任何其他自定义存储空间时在进程终止后仍能保留数据,您需要:

  • 确保键可序列化:与 rememberNavBackStack 一样,导航键必须可序列化。
  • 手动处理序列化和反序列化:您负责在应用进入后台或恢复时,手动将每个密钥的序列化表示形式保存到永久性存储空间(例如 SharedPreferences、数据库或文件),并从中反序列化。

将 ViewModel 实例限定为 NavEntry 对象

ViewModels 用于在配置更改(例如屏幕旋转)期间保留与界面相关的状态。默认情况下,ViewModels 的范围限定为最近的 ViewModelStoreOwner,通常是您的 Activity 或 Fragment。

不过,您可能希望将 ViewModel 的作用域限定为返回堆栈上的特定 NavEntry(即特定屏幕或目的地),而不是整个 Activity。这样可确保仅当特定 NavEntry 属于返回堆栈的一部分时,ViewModel 的状态才会保留,并在 NavEntry 弹出时清除。

androidx.lifecycle:lifecycle-viewmodel-navigation3 插件库提供了一个 NavEntryDecorator,可帮助您实现此目的。此修饰器为每个 NavEntry 提供一个 ViewModelStoreOwner。当您在 NavEntry 的内容中创建 ViewModel 时(例如,在 Compose 中使用 viewModel()),该 会自动 限定为返回堆栈中该特定 NavEntry 的键。这意味着,ViewModel 会在 NavEntry 添加到返回堆栈时创建,并在其移除时清除。

如需使用 NavEntryDecorator 将 ViewModel 的范围限定为 NavEntry,请按以下步骤操作:

  1. 将 androidx.lifecycle:lifecycle-viewmodel-navigation3 依赖项添加到 app/build.gradle.kts 文件中。
  2. 在构建 NavDisplay 时,将默认 rememberSaveableStateHolderNavEntryDecorator() 添加到 entryDecorators 列表中。
  3. 将 rememberViewModelStoreNavEntryDecorator() 添加到 entryDecorators 列表中。

NavDisplay(
    entryDecorators = listOf(
        // Add the default decorators for managing scenes and saving state
        rememberSaveableStateHolderNavEntryDecorator(),
        // Then add the view model store decorator
        rememberViewModelStoreNavEntryDecorator()
    ),
    backStack = backStack,
    entryProvider = entryProvider { },
)