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 数据流 / 控制流 / 信任链路
以典型的“用户按下返回键”场景为例:
- 输入捕获:KeyEvent 分发至 NavigationStackController。
- 控制流:Controller 校验是否允许回退(检查回退栈是否为空、是否有未保存状态),若允许则执行 popBackStack()。
- 数据流:页面销毁前触发 onSaveInstanceState(),状态数据写入 Bundle。
- 联动通知:NavigationStackController 发布
STACK_CHANGED事件,携带当前栈顶页面的 immersiveMode 属性。 - 状态栏响应:StatusBarController 根据接收到的事件调整可见性、颜色、透明度。
- 信任链路:两个 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 落地步骤(第一阶段)
- 盘点现有场景:梳理当前应用内所有涉及回退、首页、二级页、全屏页、沉浸页、系统弹窗等,建立场景矩阵。
- 定义联动接口:在控制层抽象
NavigationStackDelegate与StatusBarDelegate,分别暴露最小事件集合。 - 实现事件总线:选用或自建轻量级 Bus(如 LiveData + ViewModel 跨进程方案),确保主线程串行化。
- 建立测试用例:覆盖边界:空栈回退、连续快速回退、沉浸切换中中断回退。
- 模拟场景验证:使用 Espresso + SystemUI Test 框架模拟状态栏与导航栈的并发行为。
1.7 验收标准
- 功能正确性:所有回退操作后,状态栏恢复到上一页面对应的显示模式。
- 视觉平滑:状态栏显示/隐藏与页面切换动画同步,无闪白、跳变。
- 边界鲁棒:在低内存回收页面后,导航栈重建仍能按序回退,且状态栏状态正确。
- 性能基线:单次联动通知处理耗时 < 4ms(在主流设备上),不阻塞 UI 线程。