SystemUI插件开发指南

在 Android 系统中,SystemUI 是负责状态栏、通知栏、快捷开关、锁屏等核心系统 UI 的关键组件。传统上,这部分能力完全由系统内置代码实现,扩展和定制成本较高。

为了解耦系统 UI 与定制逻辑,Android 提供了 Plugin(插件)机制,允许在不修改 SystemUI 主体代码的情况下,对系统 UI 行为进行动态扩展。这也是车机、定制 ROM、厂商 UI 中非常重要的一种扩展手段。

一、什么是 SystemUI 插件?

简单说,SystemUI 插件就是:

一个运行在独立 Context / ClassLoader 中,但被 SystemUI 动态加载并调用的扩展模块

它具备几个关键特征:

动态加载:通过反射 + ClassLoader 加载 APK

接口驱动:通过接口(Plugin Interface)与 SystemUI 通信

资源隔离:插件拥有独立的资源体系(pluginContext)

生命周期托管:由 SystemUI 控制创建、销毁

你可以把它类比为:

👉 类似 Android 的"系统级热插拔组件"

二、为什么需要插件机制?

从工程角度看,插件机制解决的是三个核心问题:

1️⃣ 解耦(Decoupling)

SystemUI 主体代码不需要知道具体实现细节,只依赖接口:

java 复制代码
@ProvidesInterface(action = OverlayPlugin.ACTION, version = OverlayPlugin.VERSION)
public interface OverlayPlugin extends Plugin {

    String ACTION = "com.android.systemui.action.PLUGIN_OVERLAY";
    int VERSION = 4;

    /**
     * Setup overlay plugin
     */
    void setup(View statusBar, View navBar);
}

插件实现在插件apk中:

java 复制代码
class MyOverlayPlugin : OverlayPlugin {
    override fun setup(View statusBar, View navBar) {
        // 自定义逻辑
    }
}
<service
    android:name=".MyOverlayPlugin"
    android:exported="false"
    adroid:label="@string/plugin_label"
    tools:ignore="Instantiatable">
    <intent-filter>
        <action android:name="com.android.systemui.action.PLUGIN_OVERLAY" />
    </intent-filter>
</service>

2️⃣ 动态扩展(Extensibility)

无需修改系统源码即可扩展功能,例如:

自定义状态栏 UI

替换 QS(Quick Settings)面板

插入悬浮层(Overlay)

修改锁屏行为

3️⃣ 调试与实验(Experimentation)

插件机制天然适合:

A/B 实验

功能灰度发布

快速验证 UI/交互方案

SystemUI 如何加载插件

PluginListener PluginInstance PackageManager PluginActionManager PluginActionManager.Factory PluginManagerImpl SystemUI PluginListener PluginInstance PackageManager PluginActionManager PluginActionManager.Factory PluginManagerImpl SystemUI 存储action到mPluginPrefs loop 遍历每个插件服务 监听包安装/更新/卸载事件 addPluginListener(action, listener, cls, allowMultiple) create(action, listener, cls, allowMultiple, isDebuggable) 返回 PluginActionManager<T> 实例 loadAll() 在后台线程执行 queryAll() 检查并断开现有插件实例 清空 mPluginInstances 列表 queryIntentServices(intent) 查询插件 返回 ResolveInfo 列表 验证插件权限和有效性 create(context, appInfo, component, pluginClass, listener) 返回 PluginInstance 添加到 mPluginInstances 列表 在前台线程执行 onPluginConnected() onCreate() onPluginConnected(plugin) 同步块 - 将listener和actionManager存入mPluginMap startListening() 注册包变更广播接收器

资源及异常隔离

  1. systemuiapk 通过独立的classloader来加载,
    实现与systemui的资源隔离,
java 复制代码
        private ClassLoader getParentClassLoader(ClassLoader baseClassLoader) {
            return new PluginManagerImpl.ClassLoaderFilter(
                    baseClassLoader,
                    "androidx.constraintlayout.widget",
                    "com.android.systemui.common",
                    "com.android.systemui.log",
                    "com.android.systemui.plugin");
        }

只有这几个包资源共享,其他类插件跟systemui都是隔离的

java 复制代码
  private class PluginExceptionHandler implements UncaughtExceptionHandler {

        private PluginExceptionHandler() {}

        @Override
        public void uncaughtException(Thread thread, Throwable throwable) {
            if (SystemProperties.getBoolean("plugin.debugging", false)) {
                return;
            }
            // Search for and disable plugins that may have been involved in this crash.
            boolean disabledAny = checkStack(throwable);
            if (!disabledAny) {
                // We couldn't find any plugins involved in this crash, just to be safe
                // disable all the plugins, so we can be sure that SysUI is running as
                // best as possible.
                synchronized (this) {
                    for (PluginActionManager<?> manager : mPluginMap.values()) {
                        disabledAny |= manager.disableAll();
                    }
                }
            }
            if (disabledAny) {
                throwable = new CrashWhilePluginActiveException(throwable);
            }
        }

插件崩溃后:

立即禁用: 通过 PluginEnabler.setDisabled() 标记为禁用状态

触发重启: 包装成CrashWhilePluginActiveException异常抛给uncaughtException处理

持久化: 禁用状态会保存,下次启动也不会加载

相关推荐
海水冷却5 小时前
Android 群组聊天怎么实现:基于 ZIM 的完整落地指南(附可运行 Demo)
android
三少爷的鞋7 小时前
现代 Android 架构指南:不要再主动设计 Callback 接口
android
TimeFine13 小时前
AI 编码工作流的设计
android·ai编程
骑着蜗牛撵大象32714 小时前
C++20 协程 × 屏障:手写一个 co_await 版 Barrier,让等待不再钉死线程
android·c++20·协程·并发编程·co_await·屏障同步
山东孙燕姿16 小时前
设备台账重构:从一张表到三层模型
android·java·网络·低代码·重构·rxjava
蒸鱼Yuzheng17 小时前
移动端 CPU 调度分析:线程亲和性、大小核迁移与卡顿
android·perfetto·性能分析·线程优化·cpu调度
booboosui17 小时前
安卓唤醒词:12 维 MFCC 加 DTW,阈值 1.8
android
传奇开心果17 小时前
Compose Multiplatform 共享边界实战:架构契约、互操作同步与可回退设计
android·kotlin
事圆则缓17 小时前
从开机到首帧:Android 系统与应用启动流程源码分析
android
AI吃大瓜17 小时前
指针压力表读数4:Android实现指针压力表读数(可实时检测)
android·指针读数·指针压力表·压力表·压力表读数