组件职责与接口契约

组件化拆分的核心在于明确每个组件的边界与协作方式。本章为 SystemUI 的各个拆分组件定义对外提供的服务(接口)、所依赖的外部能力(依赖接口)、向上层或平行组件传递的“事件回调”以及组件内部或跨组件流通的“数据模型”。这些契约是后续分层设计、依赖注入和集成测试的契约基础。

4.1 组件一览

SystemUI 按业务领域拆分为以下 7 个核心组件(不含基础库层):

组件名称核心职责
StatusBar状态栏图标、系统图标、蜂窝/WiFi/电量显示、通知图标
NavigationBar三键导航(返回/Home/最近任务)、手势导航指示器
QuickSettings快捷开关面板(WiFi/蓝牙/亮度等)、编辑布局
NotificationPanel通知抽屉、通知列表管理、通知长按/滑动交互
Keyguard锁屏界面、解锁逻辑、紧急呼叫、媒体控制
VolumePanel音量弹窗、音量滑块、铃声音量/媒体音量切换
BatteryController电池状态、充电状态、省电模式、BatterySaver

4.2 接口契约定义方式

每个组件必须暴露 Interface / API 供其他组件或系统服务调用,同时声明自己依赖的 Required Interface。事件回调采用 Listener / Callback 方式向上冒泡或平行通知。数据模型分为:内部状态模型(组件的 UI 状态)和 共享数据模型(跨组件传递的不可变数据)。

设计原则:接口尽量窄(最小知识原则),回调尽量少(避免长链回调污染)。共享数据模型使用不可变对象,通过消息总线(如 LiveData / Flow)分发。

4.3 接口契约全景

下表涵盖每个组件对外提供的接口、依赖接口、事件回调和关键数据模型。契约的粒度以组件为单元,不考虑内部模块。

组件对外接口(Provided)依赖接口(Required)事件回调(Callback)数据模型
StatusBar 更新系统图标、设置是否可见 通知管理器、时钟源、电池状态 点击系统图标(蓝牙/WiFi)回调 StatusBarIcons, SystemIconState
NavigationBar 设置导航模式、禁用/隐藏按键 手势检测引擎、窗口策略 Home键/返回键/最近任务点击事件 NavigationMode, KeyEvent
QuickSettings 展开/收起面板、添加/移除Tile 设置提供者、亮度控制器 Tile点击/长按、面板滑动状态变化 QsTile, TileSpec, PanelState
NotificationPanel 显示/隐藏通知列表、更新单条通知 通知排序引擎、通知渠道管理器 通知点击、滑动删除、展开/折叠 NotificationData, NotificationRow
Keyguard 锁定/解锁、验证凭据、显示Pin/Patter 用户管理器、生物识别服务 解锁成功/失败、紧急呼叫 KeyguardState, SecurityMode
VolumePanel 显示/隐藏音量面板、设置当前音量流 音频管理器、音频策略 音量滑块拖动、流切换 VolumeStream, StreamState
BatteryController 提供当前电池百分比、充电状态 电池管理服务、省电模式策略 电池状态变化、省电模式切换 BatteryState, ChargeStatus

4.4 接口依赖与事件流关系图

以下 SVG 展示了 7 个组件之间的接口依赖(实线箭头)和重要事件回调(虚线箭头),以及共享数据模型所在的位置。注意:BatteryController 作为纯数据提供者,被多个组件依赖;Keyguard 与 NotificationPanel 之间存在锁屏状态回调。

SystemUI 组件接口依赖与事件回调图 StatusBar NavigationBar QuickSettings NotificationPanel Keyguard VolumePanel BatteryController 依赖--> BatteryController --> 依赖--> StatusBar (系统手势检测) --> 依赖--> BatteryController --> 依赖--> StatusBar (通知计数) --> 依赖--> NotificationPanel (锁屏通知) --> 依赖--> BatteryController (音量流切换?) --> 解锁/锁屏回调 通知图标更新 电池状态回调 说明: 实线箭头表示“接口依赖”(Provided/Required 关系),箭头指向被依赖方。 虚线箭头表示“事件回调”方向,从事件触发方指向监听方。 BatteryController 作为公共数据源被三个组件依赖,应利用观察者模式或共享 Flow 实现。 实线:接口依赖 虚线:事件回调
设计权衡:上图将 BatteryController 设计为“纯数据提供者”,其接口极简(仅 getBatteryState() 与 registerCallback())。这样做可以避免其他组件直接依赖电池硬件细节,也便于在测试时替换 Mock。但是,如果 BatteryController 过于集中,会形成“数据总线”的热点,需要评估更新频率。若电池状态变化极快(如充电电流波动),建议改用冷流(Cold Flow)避免背压。

4.5 数据模型的划分

数据模型分为两类:

  • 内部状态模型:仅在组件内部使用的 UI 状态,例如 QuickSettings 的“当前面板展开高度”或 Keyguard 的“密码输入错误次数”。这些模型对外透明。
  • 共享数据模型:需要跨组件传递的不可变对象,例如 BatteryState(包含百分比、充电状态、省电模式标志)在 StatusBar 和 QuickSettings 之间共享;NotificationEntry 在 NotificationPanel 和 Keyguard 之间共享。

所有共享数据模型必须满足:序列化/反序列化支持(如果跨进程)、不可变(使用 data class 或 @Immutable)、通过公共模块(如 systemui-shared-model)发布。

4.6 风险点与选型权衡

风险点表现缓解措施
循环依赖组件 A 依赖 B,B 间接依赖 A使用接口反转(DIP),将共享接口提升到公共基础库层
回调泛滥一个组件的状态变化触发过多节点的更新引入聚合事件(如 SystemUIEvent 统一分发给订阅者)
数据模型膨胀共享数据模型包含过多字段,导致耦合按“最小需要原则”拆分,每个模型只包含该场景必需字段
跨进程接口延迟接口调用涉及 IPC(如跨系统服务)在组件内部缓存状态,使用异步回调,或采用 binder 池减少连接
重要提醒:接口契约一旦制定,修改成本极高。建议在第一个版本就定义完备的接口签名,并采用“适配器模式”来兼容未来变化。不要在组件间直接暴露内部类,必须使用接口抽象层。

4.7 落地步骤与验收标准

  1. 编写每个组件的 .api 接口定义文件(可使用 Kotlin 接口或 AIDL),并放置在独立模块中。
  2. 生成接口文档(如使用 Dokka 或 Doxygen),并人工评审接口签名是否违反依赖规则。
  3. 构建单元测试框架,为每个接口提供 Mock 实现,验证事件回调方向是否正确。
  4. 集成测试:通过自动化测试模拟所有组件同时运行,检查数据模型流转是否一致。
  5. 验收标准:所有组件只能通过已定义的接口通信,不存在绕过契约的直接引用;修改接口时必须修改所有实现方,否则编译失败。

通过上述接口契约的严格定义,SystemUI 的组件化拆分才能避免“拆而不解耦”的陷阱。下一环节将基于这些契约,设计组件间通信机制和依赖注入方案。