1. 导航栈与状态栏的职责边界

在 SystemUI 架构中,导航栈与状态栏是两条独立却又频繁交汇的控制线。明确二者职责边界是保证页面回退逻辑不丢失系统状态、沉浸体验不中断的前提。本章从边界、分层、组件职责、数据流与控制流、信任链路、选型权衡、风险点、落地步骤及验收标准九个维度展开,建立第一块设计基石。

1.1 职责边界定义

维度 导航栈(NavigationStack) 状态栏(StatusBar)
核心职责 管理页面切换、回退顺序、页面生命周期 显示系统状态(网络、电量、时间)、通知、沉浸模式
数据源 Activity / Fragment 栈、自定义回退栈 系统服务(BatteryService、ConnectivityManager 等)
控制权 用户手势/系统调用驱动页面跳转 系统状态变化驱动 UI 更新、用户交互(下拉)
联接触发 页面切换时需告知状态栏调整显示策略 状态栏变更可能触发导航栈重新计算安全区域
异常场景 回退过程中页面重建导致状态丢失 沉浸模式抢占后状态栏未按预期恢复
关键原则:导航栈不应直接持有系统状态数据,状态栏不应干预页面回退逻辑。两者通过定义的联动接口交换最小必要信息。

1.2 架构分层模型

SystemUI 内部采用三层架构实现职责隔离:

  • 表现层(UI Layer):导航栈的页面容器(如 TaskStackView)、状态栏的 ViewGroup(如 PhoneStatusBarView)。
  • 控制层(Controller Layer):NavigationStackController(管理回退指令序列)、StatusBarController(管理状态更新策略)。
  • 服务层(Service Layer):WindowManager(窗口层级管理)、SystemUIService(状态分发总线)。

联动发生在控制层之间。NavigationStackController 在页面栈发生变化时发布事件,StatusBarController 订阅后调整自身显示模式;反之,状态栏的沉浸模式变更通过 SystemUIService 通知 NavigationStackController 重新计算安全区域。

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

以典型的“用户按下返回键”场景为例:

  1. 输入捕获:KeyEvent 分发至 NavigationStackController。
  2. 控制流:Controller 校验是否允许回退(检查回退栈是否为空、是否有未保存状态),若允许则执行 popBackStack()。
  3. 数据流:页面销毁前触发 onSaveInstanceState(),状态数据写入 Bundle。
  4. 联动通知:NavigationStackController 发布 STACK_CHANGED 事件,携带当前栈顶页面的 immersiveMode 属性。
  5. 状态栏响应:StatusBarController 根据接收到的事件调整可见性、颜色、透明度。
  6. 信任链路:两个 Controller 之间通过 SystemUIBus(由 SystemUIService 管理的 EventBus)通信,不直接持有对方引用。Event 格式预先定义,避免运行时类型耦合。
// 事件格式示例(伪码描述)
Event {
  type: STACK_CHANGED,
  payload: {
    stackDepth: 3,
    currentImmersiveMode: “LIGHT_STATUS_BAR”,
    pendingTransition: true
  }
}

1.4 选型权衡

方案 优势 劣势
Activity 原生栈 + 状态栏全局监听 系统级稳定、无需自建栈 无法控制沉浸模式切换的时机,事件粒度粗
自定义 Fragment 栈 + 联动中间件 精细控制页面回退与状态栏动画同步 增加维护成本、需处理屏幕旋转复原
单 Activity + Compose Navigation 声明式联动、状态易于管理 与 SystemUI 原生 View 体系耦合较深时需适配层
风险提示:若选择自定义栈方案,必须妥善处理进程重启后的栈恢复,否则回退历史会永久丢失。建议同时接入 SavedStateHandle 或持久化栈快照。

1.5 风险点清单

  • 状态栏与导航栈异步时序冲突:页面切换动画中状态栏提前消失或延后恢复,导致视觉闪烁。
  • 沉浸模式下的回退阻断:某些页面要求全屏沉浸,回退时未通知状态栏恢复导航栏,用户误触。
  • 栈深度与状态栏高度动态计算不一致:状态栏高度变更(如刘海区域、手势指示条)未同步到页面布局。
  • 多窗口模式下栈状态混乱:分屏时两个 Task 各自维护导航栈,状态栏需要感知焦点切换。

1.6 落地步骤(第一阶段)

  1. 盘点现有场景:梳理当前应用内所有涉及回退、首页、二级页、全屏页、沉浸页、系统弹窗等,建立场景矩阵。
  2. 定义联动接口:在控制层抽象 NavigationStackDelegate 与 StatusBarDelegate,分别暴露最小事件集合。
  3. 实现事件总线:选用或自建轻量级 Bus(如 LiveData + ViewModel 跨进程方案),确保主线程串行化。
  4. 建立测试用例:覆盖边界:空栈回退、连续快速回退、沉浸切换中中断回退。
  5. 模拟场景验证:使用 Espresso + SystemUI Test 框架模拟状态栏与导航栈的并发行为。

1.7 验收标准

  • 功能正确性:所有回退操作后,状态栏恢复到上一页面对应的显示模式。
  • 视觉平滑:状态栏显示/隐藏与页面切换动画同步,无闪白、跳变。
  • 边界鲁棒:在低内存回收页面后,导航栈重建仍能按序回退,且状态栏状态正确。
  • 性能基线:单次联动通知处理耗时 < 4ms(在主流设备上),不阻塞 UI 线程。
导航栈与状态栏职责边界与联动架构 导航栈与状态栏职责边界与联动架构 导航栈域 页面栈管理 回退指令仲裁 页面生命周期 安全区域计算 NavigationStackController 状态栏域 系统状态显示 沉浸模式切换 通知聚合 手势交互处理 StatusBarController 联动桥接层 STACK_CHANGED 事件 安全区域变更 两个 Controller 通过 SystemUIBus 交换最小事件,不直接持有对方引用