前言
在传统的 Android 开发中,页面跳转离不开 Intent、FragmentManager 或者是 Navigation XML。作为资深Android开发,你一定经历过处理 onFragmentResult 的痛苦,或是被深层嵌套的 Fragment 堆栈折磨得没脾气。
进入 Compose 时代,页面不再是 Activity 或 Fragment,而是一个个普通的函数。那么,我们如何管理这些函数的切换?如何处理参数传递?又如何保证导航逻辑不污染我们的 UI 架构?
今天,我们就来攻克 Navigation Compose ,并引入现代化的 类型安全导航 (Type-Safe Navigation)。
一、 思维转变:从"启动组件"到"定义目的地"
在 Compose 中,导航的核心是 NavHost。
- 旧思维:我想去 A 页面,所以我启动 Activity A。
- 新思维 :我定义了一个导航图(NavGraph),告诉它有哪些"目的地(Destinations)",然后通过改变"路径(Route)"来让
NavHost展示不同的目的地。
二、 现代方案:类型安全导航(Kotlin Serialization)
早期的 Navigation Compose 使用字符串字符串路径(String Routes),这非常容易拼错,且传参像拼 URL 一样痛苦。 现在的最佳实践是使用 Kotlin Serialization 实现全类型安全。
1. 定义目的地(Destinations)
kotlin
@Serializable
sealed class Screen {
@Serializable object Home : Screen()
@Serializable data class Profile(val userId: String) : Screen()
}
2. 构建导航图
kotlin
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home) {
composable<Screen.Home> {
HomeScreen(onUserClick = { id ->
navController.navigate(Screen.Profile(id))
})
}
composable<Screen.Profile> { backStackEntry ->
val profile = backStackEntry.toRoute<Screen.Profile>()
ProfileScreen(userId = profile.userId)
}
}
仔细观察 :看到没有?没有任何字符串拼接。参数 userId 自动从对象中解析出来,且有编译期检查。
三、 架构进阶:解耦导航逻辑
作为一名资深架构师,你绝对不希望 navController 渗透进每一个 UI 组件。这会导致你的 UI 难以单独预览和测试。
最佳实践:回调提升 (Callback Hoisting)
UI 组件只负责暴露回调,导航动作由最外层的"导航控制器"统一处理。
kotlin
@Composable
fun UserList(onUserDetail: (String) -> Unit) {
// UI 逻辑,只管点,不管跳
Button(onClick = { onUserDetail("123") }) { ... }
}
四、 跨页传参的"深水区":该传对象还是传 ID?
这是一个老生常谈的问题。在 View 体系中,我们喜欢传 Parcelable 对象。
在 Compose 导航中的建议:只传最小 ID。
- 原因 1 :导航堆栈是有存储限制的,传大对象可能导致
TransactionTooLargeException。 - 原因 2 :符合 MVI 原则。传一个
userId给目标页面的 ViewModel,由 ViewModel 去数据库或网络获取最新的数据。这能保证页面跳转后数据的一致性。
五、 给开发者的导航避坑指南
- 不要在 NavHost 外部单例化 NavController :
navController必须是remember出来的,与当前组合上下文绑定。如果你想在 ViewModel 里导航,请定义一个NavigationEvent流(Effect),由 UI 层监听并执行跳转。 - 处理深层链接 (DeepLink) : Type-safe 导航同样支持 DeepLink。你只需要在
composable块中通过参数配置。 - 嵌套导航 (Nested Navigation) : 对于大型 App,建议将导航图拆分为多个子图(Navigation DSL 中的
navigation块),这样可以有效管理模块化的代码。
结语
Navigation Compose 的出现,让 Android 开发真正实现了"单 Activity 架构"。配合类型安全机制,我们终于告别了 XML 时代的死板与不确定性。
下一篇我们将面对每一个开发者最关心的现实问题:性能优化实战:如何定位冗余重组并榨干每一帧性能。
如果你觉得有帮助,欢迎点赞关注,我们在代码上演进,在原理上深耕。