4. SystemUI 项目结构分析:关键注入点识别与服务组件

SystemUI 作为一个独立 APK 运行在系统进程中,其内部模块高度复杂,依赖关系盘根错节。正确识别 Dagger 注入的“关键入口点”与“核心服务组件”,是后续改造与扩展的基石。

4.1 项目结构概览

SystemUI 的源码位于 frameworks/base/packages/SystemUI/,核心业务逻辑集中在 src/com/android/systemui/。从依赖注入的视角,可将包结构划分为三层:

层次典型包名职责
全局注入层di定义顶级 Component、Module 及作用域注解
功能注入层statusbar, navigationbar, qs各子功能模块的 Subcomponent 与绑定声明
服务组件层plugins, shared, util被注入的 Service/Controller/Helper 实现
关键原则:所有模块的 @Module 最终都必须汇总到 SystemUIxModule(或类似聚合模块),再注入到 SysUIComponent 中。新增功能时,检查是否完成此“注册”链条。

4.2 Dagger 注入体系的全局布局

SystemUI 的 Dagger 基础设施主要依赖以下注解与基类:

  • @SysUISingleton —— 自定义作用域,等价于 @Singleton,确保对象在 SystemUI 进程内唯一。
  • @SysUIComponent —— 唯一的全局顶级 @Component,负责实例化所有 @SysUISingleton 对象。
  • @Binds / @Provides —— 分别在抽象 Module 与静态 Module 中声明绑定。
  • 核心入口类 SystemUIApplication —— 在 onCreate() 中调用 DaggerSysUIComponent.create(),启动 DI 容器。

4.3 关键注入点识别

以下 5 个注入点构成 SystemUI 的“骨架”,无论定制任何功能,都必须先确认它们的存在与配置:

序号注入点所在文件 / 注解注入时机
1SysUIComponentSysUIComponent.java
@Component(modules={…})
App 启动时
2StatusBarStatusBar.java
@Inject 构造器
系统准备完成后
3NavigationBarComponentNavigationBarComponent.java
@Subcomponent
导航栏创建时
4NotificationPanelViewNotificationPanelView.java
@Inject 字段/构造器
布局 inflate 时
5GlobalActionsComponentGlobalActionsComponent.java
@Binds 接口绑定
长按电源键触发时
识别技巧:搜索 @Inject 所在类的 create() 或 get() 调用点,找到对应的 Subcomponent.Builder,即可反推出该注入点所在的上层 Component。通常会在 DaggerSysUIComponent 生成的实现类中找到。

4.4 核心服务组件及其注入关系

SystemUI 内部存在大量长时间运行的 Service 与 Controller,它们通过 DI 汇集到 UI 层。以下是 5 个最重要的服务组件:

  • CommandQueue —— 接收系统服务(WMS/NMS)的 IPC 回调,触发 StatusBar 状态更新。
  • NotificationListener —— 监听 NotificationManagerService 的通知变化,驱动通知面板的增删改。
  • DozeServiceHost —— 管理息屏显示(Ambient Display)与唤醒逻辑。
  • BatteryController —— 提供电量/充电状态数据,注入到 QS 与状态栏图标。
  • ThemeOverlayController —— 负责动态主题(如 monet)的切换与同步。

这些服务组件通常注入了 Context、Looper(主线程或 BG 线程)、以及 DumpManager,以便在 adb shell dumpsys 输出诊断信息。

// 典型的服务组件注入构造器示例 (伪代码)
@SysUISingleton
public class BatteryControllerImpl implements BatteryController {
    private final Context context;
    private final Handler bgHandler;
    private final DumpManager dumpManager;

    @Inject
    public BatteryControllerImpl(
            Context context,
            @BgLooper Looper bgLooper,
            DumpManager dumpManager) {
        this.context = context;
        this.bgHandler = new Handler(bgLooper);
        this.dumpManager = dumpManager;
        dumpManager.registerDumpable("BatteryController", this);
    }
}

4.5 SVG:SystemUI 依赖注入架构总览

SystemUI 依赖注入关键节点与关系 SystemUI 依赖注入架构 · 关键注入点与服务组件 🔹 全局注入层 (di包) 🔹 功能注入层 (statusbar/navbar/qs) 🔹 服务组件层 (shared/util/plugins) SysUIComponent SystemUIApp StatusBar NavBarComponent BatteryCtrl 📌 关键服务组件清单 1. CommandQueue —— IPC 回调调度 2. NotificationListener —— 通知监听 3. DozeServiceHost —— 息屏管理 4. BatteryController —— 电量状态 5. ThemeOverlayController —— 主题切换 🔎 识别注意事项 · 每个 Subcomponent 必须显式声明 · Service 若注入 Context,需确认 Scope · 循环依赖通常发生在构造器注入时 · 使用 @Named 区分同类型依赖 · DumpManager 注册辅助调试

4.6 调试与验证方法

  • Dagger 注解处理器输出:编译后查看 build/generated/source/apt/ 下的 DaggerSysUIComponent.java,确认所有子组件与模块是否自动生成。
  • 注入点断点:在 SystemUIApplication.onCreate() 中 DaggerSysUIComponent.create() 处设断点,逐层展开可看到全部已注册的 @Provides 方法。
  • 运行时分析:执行 adb shell dumpsys activity service com.android.systemui/.SystemUIService,观察有注入字段的服务是否正常填充。
常见错误:忘记将新增的 @Module 加入 SysUIComponent 的 modules 数组,导致编译时提示“找不到依赖”。解决方案:逐一检查 SystemUIxModule 中的模块列表,确保新增模块已添加。

4.7 避坑指南:注入点选择与作用域配平

场景推荐注入点作用域理由
全局单例服务SysUIComponent@SysUISingleton进程内唯一,省内存
UI 控制器对应 Subcomponent@ActivityScoped / @ViewScoped随组件销毁释放
动态插件PluginComponent无插件生命周期独立
工具类无状态@Provides + @Reusable@Reusable允许缓存,不强制单例