Navigation 3 代表着 Jetpack Compose 处理导航状态方式的根本转变,与 Navigation 2 相比,它具有显著的架构优势。
了解将 Wear Compose 应用从 Navigation 2 迁移到 Navigation 3 所需的架构变更和步骤。
Navigation 3 的主要优势
- 直接返回堆栈控制:
NavBackStack从根本上来说只是 一个可变NavKey对象列表,表示用户访问过的屏幕的历史记录。您可以像控制任何 KotlinMutableList(add、removeLast、clear)一样控制它。您可以直接操纵列表来执行导航操作,例如添加键以向前移动或移除键以向后移动。 - 以 Compose 为先的设计:返回堆栈被建模为标准可观测 状态。修改导航历史记录的行为与更新任何其他 Compose 状态的行为完全相同,会自动触发重组以显示当前屏幕。
- 默认类型安全:完全消除了基于字符串的路线。 导航使用可序列化数据对象和数据类。
- 分离的演示文稿(场景策略):界面转换层
(
NavDisplay和SwipeDismissableSceneStrategy)与状态跟踪(NavBackStack)完全 分离,从而可以更轻松地集成内置的 Wear OS 导航转换。
迁移步骤
1. 更新依赖项
移除旧的 androidx.wear.compose:compose-navigation 依赖项,并引入新的拆分 Navigation 3 依赖项以及 Kotlin 序列化支持。
移除:
implementation("androidx.wear.compose:compose-navigation:...")
添加:
implementation("androidx.navigation3:navigation3-runtime:...") // State logic
implementation("androidx.navigation3:navigation3-ui:...") // Display logic
implementation("androidx.wear.compose:compose-navigation3:...") // Wear gestures
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:...") // Requires compiler plugin
2. 更新目的地以实现 NavKey
在 Navigation 2 中,您可能使用了字符串或通用对象进行路由。在
Navigation 3 中,您 必须 实现 NavKey 标记接口,并
使用 @Serializable 注解每个屏幕对象。
为什么需要这样做?为了保证返回堆栈可以在进程终止后保存和
恢复,底层 navigation3-runtime 依赖于
kotlinx-serialization 来序列化状态。
之前(Navigation 2 - 通用类型安全路线):
sealed class Nav2Screen { data object Landing : Nav2Screen() data object List : Nav2Screen() }
之后(Navigation 3 - NavKey + 可序列化):
@Serializable sealed interface MigrationScreen : NavKey { @Serializable data object Landing : MigrationScreen @Serializable data object List : MigrationScreen }
3. 替换路由逻辑(NavController 到 NavBackStack)
将 NavController 替换为通过
rememberNavBackStack 初始化的 NavBackStack。您还需要专门为 Wear OS 实例化
SwipeDismissableSceneStrategy。
之前(Navigation 2):
val navController = rememberSwipeDismissableNavController()
之后(Navigation 3):
val backStack = rememberNavBackStack(MigrationScreen.Landing as NavKey) val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()
4. 将 NavHost 替换为 NavDisplay 和 entryProvider DSL
NavHost 容器及其内部 composable("route") { ... } 构建器
DSL 将被 NavDisplay 和 entryProvider {
entry<Key> { ... } } DSL 替换。
之前(Navigation 2):
SwipeDismissableNavHost(navController = navController, startDestination = "menu") { composable("menu") { GreetingScreen( onShowList = { navController.navigate("list") } ) } composable("list") { ListScreen() } }
之后(Navigation 3):
NavDisplay( backStack = backStack, sceneStrategies = listOf(strategy), entryProvider = entryProvider { entry<MigrationScreen.Landing> { GreetingScreen( onShowList = { backStack.add(MigrationScreen.List) } ) } entry<MigrationScreen.List> { ListScreen() } } )