保存和管理导航状态

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

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

确保您的应用的导航状态在各种生命周期事件(包括配置更改和进程终止)中持久存在,对于良好的用户体验至关重要。在 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)
}

替代方案:存储在 ViewModel

管理返回堆栈的另一种方法是将其存储在 ViewModel 中。在使用 ViewModel 或任何其他自定义存储时,若要实现进程终止后的持久性,您需要

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

ViewModel 作用域限定到 NavEntry

ViewModel 用于在配置更改(例如屏幕旋转)中保留与界面相关的状态。默认情况下,ViewModel 的作用域限定为最近的 ViewModelStoreOwner,通常是您的 ActivityFragment

但是,您可能希望将 ViewModel 的作用域限定为返回堆栈上的特定 NavEntry(即特定屏幕或目标),而不是整个 Activity。这可确保 ViewModel 的状态仅在该特定 NavEntry 位于返回堆栈中时保留,并在 NavEntry 被弹出时清除。

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

要使用 NavEntryDecoratorViewModel 的作用域限定到 NavEntry,请按以下步骤操作

  1. androidx.lifecycle:lifecycle-viewmodel-navigation3 依赖项添加到您的 app/build.gradle.kts 文件中。
  2. 构建 NavDisplay 时,将 rememberSavedStateNavEntryDecorator() 添加到 entryDecorators 列表中。
  3. 将其他装饰器添加到您的 NavDisplay 中。

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