NavHost 与 NavController 职责划分:生命周期绑定、导航栈管理、通信通道
在 Navigation 组件中,NavHost 与 NavController 是最核心的两个运行时角色。它们的职责边界决定了架构的分层方式:谁负责 UI 附着?谁负责导航逻辑?谁管理返回栈?谁处理深层链接? 本章围绕模块边界、分层职责、数据流与控制流、风险点与落地步骤,拆解这对核心搭档的协作模型。
模块边界与分层职责
| 维度 | NavHost | NavController |
|---|---|---|
| 层级定位 | UI 层容器 | 导航控制层 |
| 生命周期 | 绑定 Activity/Fragment 生命周期 | 独立于 UI,通过 LifecycleOwner 感知 |
| 导航栈管理 | 不直接操作返回栈 | 维护 NavBackStackEntry 栈 |
| 通信通道 | 作为 NavController 的宿主 | 提供 navigate()、popBackStack()、SavedStateHandle |
| 深层链接处理 | 解析 Intent 并委托给 Controller | 处理匹配逻辑与栈重建 |
架构原则:NavHost 是 哑容器,不包含导航决策逻辑;NavController 是 状态中心,所有导航操作最终由它执行。这种分离使得同样一套导航逻辑可复用于不同 UI 框架(Compose / Fragment)。
生命周期绑定机制
NavHost 在其 onCreate / onStart / onResume 等回调中,将自身生命周期状态同步给 NavController。NavController 内部维护一个 LifecycleObserver,用来驱动 NavBackStackEntry 的状态流转。
- 附着点:NavHost 在
onCreate中创建 NavController 并调用setLifecycleOwner(this)。 - 状态传播:Activity/Fragment 生命周期变化 → NavHost 观察者 → NavController 通知栈中所有 Entry。
- 安全退出:当宿主生命周期进入
DESTROYED,NavController 自动清空导航栈,防止内存泄漏。
关键代码片段(仅示意边界,非完整实现):
// NavHostFragment 内部
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
navController = NavController(context)
navController.setLifecycleOwner(this)
navController.setOnNavigatedListener { /* 回调 */ }
}
导航栈管理职责
NavController 维护一个 Deque<NavBackStackEntry> 作为导航栈。每个 Entry 持有目标目的地 ID、参数、SavedStateHandle。NavHost 不直接操作栈,而是通过 NavController 暴露的方法间接管理:
navigate(destinationId, args)→ 压入新 Entry 并触发 Fragment 创建/切换。popBackStack()→ 弹出栈顶 Entry,若栈空则通知宿主 finish。getBackStackEntry(destinationId)→ 允许跨层级获取已有 Entry 的 SavedStateHandle。
风险点:多个 NavHost 实例(如 ViewPager 内嵌)会导致栈混乱。每个 NavController 的栈是独立的,跨容器导航需通过 Parent NavController 代理。
通信通道与数据流
NavController 提供三种通信机制,NavHost 仅负责传递宿主的 ActivityResultRegistry 和 ViewModelStoreOwner:
- Navigation 参数:通过
NavArgs在 navigate() 时序列化传递,适合轻量数据。 - SavedStateHandle:每个 Entry 自带 Bundle 容器,适合跨配置变更保留状态。
- Shared ViewModel:借助 NavHost 提供的 ViewModelStoreOwner 实现,适合共享业务逻辑。
数据流方向:
源 Fragment → NavArgs/SavedStateHandle → NavController → 目标 Fragment
Shared ViewModel → Activity scope → 所有子 Fragment。
选型权衡
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 简单单 Activity 多 Fragment | FragmentContainerView + NavHostFragment | 开箱即用,生命周期自动绑定 |
| Compose 界面 | NavHost composable | Compose 专用实现,无 Fragment 开销 |
| 多 Activity 协作 | 每个 Activity 独立 NavController | 避免栈泄漏,Activity 边界清晰 |
| 深层链接唤醒 | NavController.handleDeepLink() | 统一解析入口,避免手动栈构建 |
风险点与应对策略
- 生命周期滞后:在 NavHost 未完全初始化前调用 navigate() 可能抛出异常。→ 使用
mOnNavigatedListener或延迟执行。 - 栈状态不一致:系统重建 Activity 时,NavController 通过
saveState()/restoreState()恢复,若自定义保存逻辑不完善则数据丢失。→ 确保 SavedStateHandle 中的关键数据可序列化。 - 通信通道膨胀:过度使用 SavedStateHandle 传递大对象会占用 Bundle 配额。→ 优先使用 ViewModel + Repository 模式。
落地步骤
- 在布局中声明
<androidx.fragment.app.FragmentContainerView>并指定android:name="androidx.navigation.fragment.NavHostFragment"。 - 在 Activity 中获取
NavHostFragment.findNavController(this)得到 NavController 实例。 - 配置 NavGraph 资源文件,绑定起始目的地。
- 在需要导航的位置调用
navController.navigate(R.id.action_target)。 - 集成深层链接:在
AndroidManifest.xml添加 intent-filter,并在 Activity 的onCreate中调用navController.handleDeepLink(intent)。
验收标准
- 生命周期一致性:旋转屏幕后返回栈顺序不变,当前 Fragment 状态保留。
- 导航栈完整性:连续 navigate 后按返回键能逐级回退,直至退出 Activity。
- 通信可靠性:通过 SavedStateHandle 传递的数据在配置变更后仍然可用。
- 深层链接正确性:外部 URL 或 Intent 能直接跳转到目标目的地,且返回栈按预期重建。