第4章 Navigation与Activity/Fragment集成:宿主容器选择、视图层级、跨组件通信

本章聚焦 Navigation 组件在 Android 原生视图架构中的落地方式:如何选择宿主容器、组织视图层级,以及在前端碎片化场景下实现高效的跨组件通信。这是将抽象导航框架绑定到具体 Activity/Fragment 生命周期的关键环节。

4.1 模块边界与分层职责

集成后的视图架构分为三层,每层有清晰的边界与职责:

  • 宿主层(Activity):作为唯一的窗口宿主,负责创建 NavHostFragment、持有全局 NavController,并处理系统级事件(如返回键、配置变更)。
  • 导航层(NavHostFragment):作为 Fragment 容器,管理 Fragment 的添加、替换、入栈出栈,维护回退栈,向宿主层报告导航状态变化。
  • 业务层(Fragment):聚焦 UI 渲染与用户交互,通过 NavController 触发导航,通过 SavedStateHandle 或 SharedViewModel 实现跨 Fragment 数据传递。

4.2 组件职责矩阵

组件核心职责边界限制
NavHostFragment承载 NavGraph,管理 Fragment 生命周期不直接操作业务数据
NavController处理导航指令,维护回退栈不持有任何 Fragment 引用
NavGraph定义目的地与导航规则仅为配置 DSL,无运行时状态
Fragment呈现 UI,响应导航事件不直接操纵其他 Fragment

4.3 视图层级与宿主容器选择

宿主容器可以选择单 Activity + 单一 NavHostFragment(推荐)或多 Activity + 各自 NavHostFragment。对比权衡如下:

维度单 Activity + 单 NavHost多 Activity + 多 NavHost
视图层级扁平,Fragment 栈深度易控层级深,Activity 栈与 Fragment 栈叠加
跨组件通信共享 ViewModel + 作用域绑定需 Intent + 序列化,开销大
回退栈一致性NavController 统一管理系统 backstack 与 Nav 回退栈冲突风险
配置变更处理单一宿主,状态恢复简单多宿主需分别处理
推荐场景大多数中大型应用模块独立分包或旧项目渐进迁移
若选择多 Activity 方案,务必禁用各 Activity 中 NavHostFragment 的默认回退栈,避免双重 onBackPressed 冲突。

4.4 数据流 / 控制流 / 信任链路

一次典型导航事件的数据流如下:

  1. Fragment A 调用 findNavController().navigate(…)。
  2. NavController 解析 NavGraph 中的目标,触发 NavHostFragment 执行 Fragment 替换。
  3. Fragment B 被创建并添加到容器,A 被加入回退栈(若设定了 pop 行为)。
  4. 若需要回传结果,B 在生命周期内通过 SavedStateHandle.set("key", data) 写入,A 在 onResume 中通过 getCurrentBackStackEntry()?.savedStateHandle?.getLiveData("key") 接收。

信任链路:
Fragment → NavController → NavHostFragment → FragmentManager
所有导航变更需经 NavController 校验,不允许绕过(如直接操作 FragmentManager),以此保证状态一致性。

4.5 跨组件通信架构

官方推荐的通信模式(按适用范围排序):

  • 共享 ViewModel (作用域限定在 Activity):适合兄弟 Fragment 间数据同步,生命周期由宿主控制。
  • SavedStateHandle (Navigation 原生):适合跨目的地回传简单值,无需引入额外依赖。
  • Navigation Result API:利用 NavBackStackEntry 的 SavedStateHandle,是官方推荐的标准回传方式。
  • EventBus / LiveDataBus:需谨慎使用,避免事件泄漏与生命周期错配。
// 结果回传示例(Fragment B)
val result = Bundle().apply { putString("data", "hello") }
findNavController().previousBackStackEntry?.savedStateHandle?.set("result", result)

// Fragment A 接收(推荐在 onViewCreated 中注册)
val savedStateHandle = findNavController().currentBackStackEntry?.savedStateHandle
savedStateHandle?.getLiveData<Bundle>("result")?.observe(viewLifecycleOwner) { bundle ->
    // 处理回传数据
}

4.6 选型权衡关键指标

决策项权衡点推荐配置
宿主容器数量1 vs N1(单 Activity)
NavHost 实例数1(嵌套 vs 非嵌套)1(避免嵌套 NavHost 复杂度)
Fragment 生命周期作用域Activity 级别 vs NavBackStackEntry 级别优先 NavBackStackEntry 级别控制
跨组件通信方式SharedViewModel vs SavedStateHandle简单数据用 SavedStateHandle,复杂用 SharedViewModel

4.7 架构风险点与应对

  • Fragment 生命周期与导航状态不同步:在 onViewCreated 之后才安全调用 navigate(),避免在 onCreate 中触发导航导致状态丢失。
  • 回退栈无限增长:合理设置 popUpTo 和 popUpToInclusive,避免栈深度无限累积。
  • 跨组件通信内存泄漏:使用 viewLifecycleOwner 注册 Observer,避免引用 Activity 级别的 ViewModel 后忘记清除。
  • 嵌套 NavHostFragment 的焦点混乱:避免在同一 Activity 内嵌套多个 NavHostFragment,若必须则需手动处理焦点管理。
推荐在 BaseFragment 中统一封装 findNavController() 的异常捕获,防止 Fragment 未附加时崩溃。

4.8 落地步骤

  1. 创建宿主 Activity:在布局中声明 NavHostFragment,指定默认 navGraph。
  2. 定义 NavGraph:用 DSL 或 XML 声明所有 Fragment 目的地及导航动作。
  3. 迁移 Fragment 至单 Activity:将原多 Activity 中的 Fragment 集中到同一 Graph,调整 navigate() 参数。
  4. 统一通信层:按数据复杂度选择 SharedViewModel 或 SavedStateHandle,封装 BaseViewModel 提供作用域绑定。
  5. 测试回退栈行为:手动执行深度导航(A→B→C→D),验证 popUpTo 和系统返回键行为。
  6. 收尾:事件上报与状态持久化:将导航路径协议化,用于分析埋点或 DeepLink。

4.9 验收标准

  • 导航一致性:所有 Fragment 的 NavController.navigate() 均能正确触发目标创建,无方向性崩溃。
  • 生命周期正确性:Fragment 的 onStart/onStop 与导航事件对齐,无内存泄漏。
  • 跨组件通信无丢失:回传数据在配置变更(如旋转屏幕)后仍能正确到达目标 Fragment。
  • 回退栈边界:从顶层按返回键,每一步都弹出预期 Fragment,最终退出 Activity 而非黑屏。

4.10 架构关系图

Navigation 集成视图层级与通信架构 Activity (宿主容器) NavHostFragment Fragment A Fragment B Fragment C NavController NavGraph 加载路由规则 跨组件通信通道 SharedViewModel / SavedStateHandle / Navigation Result API
图中虚线表示跨组件通信的数据流,实线表示导航控制流。注意 NavController 作为唯一管控入口,避免直接操作 FragmentManager。