3、顶层模块边界定义:基于功能域划分核心组件与共享基础层
SystemUI 的组件化拆分首先需要回答一个问题:“软件单元应该按什么维度切分?” 本章遵循功能域驱动原则,将用户可感知的独立交互区域或后台功能模块作为一级拆分单元,同时提取所有组件共同依赖的资源、工具与抽象层作为共享基础层。这种划分方式保证了高内聚、低耦合,使每个组件可以独立开发、测试和部署。
3.1 核心组件清单与职责边界
以下五个核心组件直接映射为 Android 系统 UI 的可见实体,它们之间的边界由交互区域不重叠、数据源不耦合的原则确定。
| 组件名称 | 功能职责 | 主要输入(事件/数据) | 对外输出 | 依赖共享基础层 |
|---|---|---|---|---|
| 状态栏(StatusBar) | 显示系统状态图标、时间、电池、信号;处理轻触展开通知面板 | 系统广播(时间、电量、信号强度) | 展开手势信号;图标布局快照 | 资源加载、图标绘制、动画模块 |
| 导航栏(NavigationBar) | 呈现三键(返回/主页/最近任务)或手势指示器;处理按键事件 | 触摸事件、系统手势模式变更 | 按键事件分发、手势区域坐标 | 触控反馈、窗口布局策略 |
| 通知面板(NotificationPanel) | 展示通知列表、分组折叠、快捷回复;可展开/折叠 | 通知管理器推送、用户滑动事件 | 通知操作结果、面板展开状态 | 通知数据模型、列表适配器、阴影渲染 |
| 锁屏(Keyguard) | 锁定屏幕时的交互界面:解锁、紧急呼叫、时钟、通知概览 | 锁屏状态、指纹/人脸识别结果、用户输入 | 解锁信号、安全事件通知 | 生物识别集成、屏幕管理策略、动画过渡 |
| 快捷设置(QuickSettings) | 提供 Wi-Fi、蓝牙、亮度、旋转等快捷开关;可编辑排序 | 系统设置变更、用户点击/长按 | 设置变更请求、面板关闭信号 | 开关图标库、共享偏好存取、动画轨道 |
边界校验准则:若某个功能的修改需要同时改动两个组件才能完成,说明功能域划分存在重叠。例如,“显示通知数量”在状态栏和通知面板中各有涉及,此时应将数量聚合逻辑下放到共享基础层,保持两个组件仅依赖同一个数据模型。
3.2 共享基础层:组件的“地基”
共享基础层不包含任何业务逻辑,它提供所有组件都需要的通用能力,避免重复实现。主要包含四大子领域:
- 资源与主题管理:颜色、字体、尺寸、Dimens、Drawable、字符串统一由基础层加载并提供给组件。
- 公共 UI 工具库:复用组件(如圆形图标、进度条、可拖拽面板)、动画插值器、阴影生成器、触摸事件拦截器。
- 数据模型与协议:通知模型、系统状态枚举、广播事件契约(如
Intentaction 常量)、接口定义(如面板展开监听器)。 - 跨进程通信抽象:封装 Binder 调用、SystemUI 内部的服务代理,使组件无需直接处理
IBinder。
共享基础层的引入使组件间的通信不再直接依赖,而是通过接口 + 事件总线的方式间接完成。例如,状态栏需要知道锁屏是否激活——它不会直接调用锁屏对象的方法,而是通过基础层的 KeyguardStateProvider 观察状态变化。
3.3 组件间的数据流与控制流
即使基于功能域划分,组件之间仍存在必要的协作。我们将其归纳为三种典型流向:
- 垂直数据流:共享基础层向下游组件推送系统状态(时间、电量、通知数)。
- 水平控制流:组件间通过事件总线发出指令。例如,“快捷设置”点击“飞行模式”开关后,通过总线通知“状态栏”更新图标;“通知面板”折叠时通知“状态栏”恢复成全透明背景。
- 跨场景重定向:锁屏下的通知点击需要先验证身份,此时锁屏组件接管控制权,待解锁完成后将事件转发给通知面板。
采用事件总线而不是直接方法调用的核心原因:降低编译时依赖,允许组件以动态插件形式加载,为后续的按需加载和热修复提供基础。
3.4 选型权衡与风险点
- 粒度权衡:将通知面板拆分为“列表区”和“折叠区”是否值得?在初期保持粗粒度划分,等性能瓶颈出现后再进行二级拆分更稳妥。
- 数据冗余风险:多个组件同时持有同一份状态缓存(如蓝牙开关状态),可能导致不同步。解决方案:让共享基础层提供单一的
StateHolder,组件只读不写。 - 事件总线膨胀:随着组件增多,事件类型可能爆炸。建议使用领域事件分组(如
Event.System、Event.Notification、Event.Input),保持可搜索性。 - 锁屏优先级:锁屏组件拥有最高渲染优先级,但它不能阻塞其他组件的后台事件处理。应确保锁屏组件与状态栏、通知面板的线程模型隔离。
3.5 落地步骤与验收标准
三步落地方案:
- 阶段一(1~2周):代码移动与接口提取。将现有 SystemUI 代码按功能域搬移到新包路径下,同时将公共资源、工具类、数据模型抽成独立的
shared模块。 - 阶段二(2~3周):事件总线接入与依赖反转。移除组件间的直接 import,改为事件发布/订阅;通过测试验证所有交互场景与原实现一致。
- 阶段三(1周):独立编译与验证。每个组件可以单独编译成 APK 或动态特性模块,运行在 Android Studio 的
systemui:component:run任务下。
验收标准清单:
- 每个核心组件不直接引用其他组件的 Java/Kotlin 类(接口引用允许)。
- 所有资源引用通过
R.shared.*或R.component.*区分,无混合使用。 - 运行 Monkey 测试 8 小时,无因组件间通信导致的 ANR 或 Crash。
- 修改一个组件的图标样式不影响其他组件的布局。
常见陷阱:切忌在共享基础层中放置“看起来通用但实际上只被一个组件使用”的代码。这类代码应留在原组件中,待第二个消费者出现时再上提。否则基础层会迅速退化为“垃圾场”,破坏架构整洁性。