OpenHarmony SystemUI 系统应用分析
目录
1. 概述
SystemUI 是 OpenHarmony 预置的系统应用程序,为用户提供系统状态展示与交互界面,包括状态栏、系统时间、电量、通知、控制中心、导航栏等核心系统 UI 元素。
2. 功能模块
| 功能模块 | 说明 |
|---|---|
| 状态栏 (StatusBar) | 显示信号、Wi-Fi、电池、时钟、铃声模式、飞行模式、位置等图标 |
| 控制中心 (ControlCenter) | 聚合快捷 toggles(Wi-Fi、蓝牙、亮度、飞行模式等) |
| 导航栏 (NavigationBar) | 底部导航条(Home/Back/Recent) |
| 下拉面板 (DropdownPanel) | 下拉通知与快速设置的混合面板 |
| 通知面板 (NotificationPanel) | 通知展示 |
| 音量面板 (VolumePanel) | 音量调节 slider |
| 窗口管理 | 系统窗口创建、显示、隐藏、销毁 |
3. 目录结构
systemui/
├── common/ # 公共模块(共享代码)
│ └── src/main/ets/
│ ├── default/ # 核心工具类
│ │ ├── WindowManager # 窗口生命周期管理
│ │ ├── ScreenLockManager # 屏幕开关状态监听
│ │ ├── TimeManager # 时间/时区管理
│ │ ├── SwitchUserManager # 多用户切换
│ │ ├── SettingsUtil # 系统设置读写
│ │ ├── EventManager # 全局事件总线
│ │ ├── EventBus # 发布-订阅模式
│ │ ├── EventUtil / EventManager # 事件解析与分发
│ │ ├── Log # 日志封装
│ │ ├── Constants # 常量定义
│ │ ├── TintStateManager # 色调状态
│ │ ├── CommonStyleManager # 样式管理
│ │ ├── MultimodalInputManager # 多模输入
│ │ ├── ReadConfigUtil # 配置读取
│ │ ├── Decorators # 装饰器(含 debounce)
│ │ └── SingleInstanceHelper # 单例辅助
│ ├── abilitymanager/ # FA 能力封装
│ │ ├── abilityManager.ts # Ability 上下文管理
│ │ ├── bundleManager.ts # Bundle 管理
│ │ ├── featureAbilityManager.ts # Feature 管理
│ │ └── notificationManager.ts # 通知管理
│ ├── event/ # 事件系统
│ │ ├── EventManager.ts # 事件分发(local/ability/commonEvent/remote)
│ │ ├── EventBus.ts # 发布-订阅总线
│ │ └── EventUtil.ts # 事件工具
│ ├── plugindatasource/ # 插件数据源(核心扩展机制)
│ │ ├── PluginDataSourceManager.ts # 插件数据源管理器
│ │ ├── PluginDataSourceAdapter.ts # 适配器,连接 Manager 与 Service
│ │ ├── sourceloader/
│ │ │ ├── SourceLoader.ts # 源加载基类
│ │ │ ├── MetaSourceLoader.ts # 元数据源加载(系统内建)
│ │ │ ├── PluginSourceLoader.ts # 插件源加载(第三方扩展)
│ │ │ └── SourceLoaderFactory.ts # 工厂类
│ │ └── common/
│ │ ├── BundleParseUtil.ts # Bundle 解析、Ability 查询
│ │ └── Constants.ts # 插件数据结构定义
│ └── template/ # UI 模板
├── features/ # 19 个功能组件
│ ├── airplanecomponent/ # 飞行模式组件
│ ├── autorotatecomponent/ # 自动旋转组件
│ ├── batterycomponent/ # 电池组件
│ ├── bluetoothcomponent/ # 蓝牙组件
│ ├── brightnesscomponent/ # 亮度组件
│ ├── capsulecomponent/ # 通知胶囊
│ ├── clockcomponent/ # 时钟组件
│ ├── controlcentercomponent/# 控制中心组件
│ ├── locationcomponent/ # 位置组件
│ ├── managementcomponent/ # 管理组件
│ ├── navigationservice/ # 导航服务
│ ├── nfccomponent/ # NFC 组件
│ ├── noticeitem/ # 通知项
│ ├── ringmodecomponent/ # 铃声模式
│ ├── signalcomponent/ # 信号组件
│ ├── statusbarcomponent/ # 状态栏组件
│ ├── volumecomponent/ # 音量组件
│ ├── volumepanelcomponent/ # 音量面板组件
│ └── wificomponent/ # Wi-Fi 组件
├── product/ # 产品/设备差异化配置
│ ├── default/ # 默认配置
│ ├── phone/ # 手机设备
│ │ ├── statusbar/ # 状态栏配置(布局、尺寸)
│ │ ├── navigationBar/ # 导航栏配置
│ │ ├── dropdownpanel/ # 下拉面板配置
│ │ ├── notificationmanagement/ # 通知管理
│ │ ├── dialog/ # 对话框
│ │ └── volumepanel/ # 音量面板
│ └── pc/ # PC 设备
├── entry/ # 应用入口(分设备 phone/pc)
│ ├── phone/
│ │ └── src/main/ets/
│ │ ├── Application/AbilityStage.ts # Stage 入口
│ │ ├── ServiceExtAbility/ServiceExtAbility.ts # ServiceExtension
│ │ └── pages/index.ets # 主页面(空壳,UI 在 product 中)
│ └── pc/
└── doc/Instructions.md # 使用说明
4. 架构设计
4.1 整体架构
┌─────────────────────────────────────────────────────┐
│ ServiceExtAbility (主入口) │
│ onCreate → initSystemUi() │
└──────────────────────┬──────────────────────────────┘
│ 设置 context
┌──────────────▼──────────────────┐
│ EventManager (全局总线) │
│ local / ability / commonEvent │
├──────────────┬───────────────────┤
│ WindowManager │ ScreenLockMgr │
│ TimeManager │ SettingsUtil │
│ SwitchUserManager │
└──────┬─────────┴─────────────────┘
│ 插件数据源
┌──────────▼──────────────────────────┐
│ PluginDataSourceManager │
│ 1. 监听 bundle 安装/卸载/更新 │
│ 2. 查询已注册 Ability │
│ 3. 分发给 SourceLoader │
└──────┬──────────────┬───────────────┘
│ │
┌──────▼──────┐ ┌────▼──────────────────────┐
│MetaSourceLoader│ │PluginSourceLoader │
│ (系统内建) │ │ (第三方扩展) │
└──────────────┘ └─────────────────────────┘
│ │
└──────────┬──────────┘
│ onItemAdd / onItemRemove
┌─────────────▼──────────────────────┐
│ StatusBarService / ControlCenterService │
│ (各 feature 的 Service 层) │
└────────────────┬──────────────────┘
│ ViewModel / Listener
┌──────────▼──────────────┐
│ Feature UI Components │
│ (ets/pages/*.ets) │
└─────────────────────────┘
4.2 核心类说明
| 类 | 职责 |
|---|---|
WindowManager |
管理系统 9 类窗口的创建、移动、显示、隐藏、销毁;映射 WindowType → Window.WindowType |
ScreenLockManager |
订阅 COMMON_EVENT_SCREEN_OFF/ON,发布 screenChangeEvent |
SwitchUserManager |
监听账户切换事件,维护当前用户状态 |
PluginDataSourceManager |
管理插件数据源:监听应用安装卸载、查询 Ability、路由到对应 Loader |
PluginDataSourceAdapter |
桥接 Manager 与各 Feature Service(StatusBar/ControlCenter) |
MetaSourceLoader |
加载系统内建组件(通过配置中的 MetaSlots) |
PluginSourceLoader |
加载第三方动态插件组件 |
SourceLoaderFactory |
根据配置类型创建对应 SourceLoader 实例 |
BundleParseUtil |
查询 bundleManager 中指定 action 的 Ability,注册包变更监听 |
EventManager |
统一事件分发:支持 local/ability/commonEvent/remote 四种 target |
EventBus |
进程内发布-订阅,支持 on/once/off/emit |
SettingsUtil |
读写系统设置项(如快捷布局配置) |
4.3 插件扩展机制
SystemUI 的核心扩展性体现在 插件数据源。第三方应用通过声明以下 Metadata 向 SystemUI 注册功能组件:
{
"name": "metaData",
"items": [
{
"name": "action",
"value": "com.ohos.systemui.action.BAR_ICON"
}
]
}
PluginDataSourceManager 监听 COMMON_EVENT_PACKAGE_ADDED/REMOVED/CHANGED,自动发现注册了特定 action 的 Ability,并通过对应的 SourceLoader 加载/卸载插件组件。
| Action | 用途 |
|---|---|
com.ohos.systemui.action.BAR_ICON |
状态栏图标插件 |
com.ohos.systemui.action.TOGGLE |
控制中心 Toggle 插件 |
5. 系统依赖与权限
5.1 系统能力依赖
| 系统能力 | 用途 |
|---|---|
@ohos.window |
创建/管理系统窗口 |
@ohos.commonEvent |
订阅屏幕开关、包安装卸载等公共事件 |
@ohos.bundle.bundleManager |
查询 Ability 信息 |
@ohos.settings |
读写系统设置项 |
@ohos.account.osAccount |
多用户管理 |
@ohos.app.ability.common |
UIAbilityContext / ServiceExtensionContext |
@ohos.app.ability.AbilityStage |
Stage 模型入口 |
5.2 权限列表
entry/phone/src/main/module.json5 中声明的主要权限:
| 权限 | 用途 |
|---|---|
ohos.permission.GET_BUNDLE_INFO_PRIVILEGED |
获取任意应用包信息 |
ohos.permission.GET_INSTALLED_BUNDLE_LIST |
获取已安装包列表 |
ohos.permission.MANAGE_LOCAL_ACCOUNTS |
管理本地账户 |
ohos.permission.NOTIFICATION_CONTROLLER |
通知控制 |
ohos.permission.GET_WIFI_INFO / SET_WIFI_INFO |
Wi-Fi 信息读写 |
ohos.permission.MANAGE_WIFI_CONNECTION |
Wi-Fi 连接管理 |
ohos.permission.USE_BLUETOOTH / MANAGE_BLUETOOTH |
蓝牙管理 |
ohos.permission.CAPTURE_SCREEN |
截屏 |
ohos.permission.MODIFY_AUDIO_SETTINGS |
音频设置 |
ohos.permission.GET_TELEPHONY_STATE |
电话状态 |
ohos.permission.CONNECTIVITY_INTERNAL |
网络连接 |
ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS_EXTENSION |
跨账户交互 |
ohos.permission.DISTRIBUTED_DATASYNC |
分布式数据同步 |
ohos.permission.START_INVISIBLE_ABILITY |
启动不可见 Ability |
ohos.permission.START_ABILITIES_FROM_BACKGROUND |
后台启动 Ability |
ohos.permission.PERMISSION_USED_STATS |
权限使用统计 |
ohos.permission.ACCESS_SERVICE_DM |
设备管理服务 |
ohos.permission.ACCESS_NOTIFICATION_POLICY |
通知策略访问 |
6. 启动流程与入口组件
1. 系统启动 ServiceExtAbility
└→ onCreate()
├→ initSystemUi(context)
│ ├→ EventManager.setContext(context)
│ ├→ ScreenLockManager.init() // 订阅屏幕事件
│ └→ TimeManager.init(context)
└→ AbilityManager.setContext(ABILITY_NAME_ENTRY, context)
2. 各 Product Module 的 ServiceExtAbility 启动
└→ 各自 onCreate() (创建系统窗口 + 注册上下文)
│ 注:窗口 'pages/index' 加载时,会触发对应 feature 层的 Service
├→ StatusBarService.startService(config, moduleName)
│ └→ PluginDataSourceAdapter.initDataSource()
│ └→ PluginDataSourceManager.initDataSource()
│ ├→ registerBundleListener() // 监听包事件
│ └→ queryAbility() // 查询已安装 Ability
├→ ControlCenterService.startService(config, moduleName)
└→ NavigationBarService.startService(config, moduleName)
3. PluginDataSourceManager 收到包事件
└→ onBundleNotify()
├→ BUNDLE_ADD: queryAbility() → notifyAbilityAdd() → Loader.onAbilityAdd()
└→ BUNDLE_REMOVE/CHANGE: Loader.onBundleRemove()
6.2 ServiceExtAbility 详解
是什么
ServiceExtAbility 是 SystemUI 的主入口组件,继承自 ServiceExtension(Stage 模型下的纯后台服务)。它没有 UI 界面,以服务形式在后台常驻运行,负责初始化 SystemUI 的全部基础设施和子模块。
// ServiceExtAbility.ts(精简)
import ServiceExtension from '@ohos.app.ability.ServiceExtensionAbility';
class ServiceExtAbility extends ServiceExtension {
onCreate(want: Want): void {
initSystemUi(this.context); // 初始化全局事件总线、屏幕监听、时间服务
AbilityManager.setContext( // 注册 Ability 上下文镜像
AbilityManager.ABILITY_NAME_ENTRY, this.context
);
}
}
在 module.json5 中注册为:
"extensionAbilities": [{
"name": "com.ohos.systemui.ServiceExtAbility",
"type": "service",
"srcEntrance": "./ets/ServiceExtAbility/ServiceExtAbility.ts"
}]
除主入口外,各 Product Module(statusbar、navigationbar、dropdownpanel 等)也有各自的 ServiceExtAbility,AMS 分别启动它们,各自管理对应窗口的生命周期:
| ServiceExtAbility 名称 | 职责 |
|---|---|
com.ohos.systemui.ServiceExtAbility |
主入口,初始化全局系统 |
com.ohos.systemui.statusbar.ServiceExtAbility |
状态栏窗口创建与管理 |
com.ohos.systemui.navigationbar.ServiceExtAbility |
导航栏窗口创建与管理 |
com.ohos.systemui.dropdownpanel.ServiceExtAbility |
下拉面板窗口创建与管理 |
AMS 如何感知 SystemUI
AbilityManagerService 在源代码中硬编码了 SystemUI 的信息:
// ability_runtime/services/abilitymgr/include/ability_config.h
constexpr const char* SYSTEM_UI_BUNDLE_NAME = "com.ohos.systemui";
constexpr const char* SYSTEM_UI_ABILITY_NAME = "com.ohos.systemui.ServiceExtAbility";
constexpr const char* SYSTEM_UI_STATUS_BAR = "com.ohos.systemui.statusbar.ServiceExtAbility";
constexpr const char* SYSTEM_UI_NAVIGATION_BAR = "com.ohos.systemui.navigationbar.ServiceExtAbility";
SystemUI 被 AMS 识别为系统级组件,享受特殊策略:保活、崩溃重启、开机自动拉起。
启动时机(两个触发点)
① 系统开机 — AMS 自动拉起
系统开机流程
↓
init → AppSpawn → SAMGR 注册系统服务
↓
AbilityManagerService::OnStart()
├── InitSubManagers()
├── 启动 Launcher(桌面)
├── 启动 SystemUI ServiceExtAbility ← 开机序列中自动发起
│ ├── com.ohos.systemui.ServiceExtAbility
│ ├── com.ohos.systemui.statusbar.ServiceExtAbility
│ ├── com.ohos.systemui.navigationbar.ServiceExtAbility
│ └── ...
└── 启动其他系统服务
② 崩溃恢复 — AMS 自动重启
AMS 将 SystemUI 注册为保留服务(keep-alive),进程异常退出后 AMS 立即重新拉起:
// ability_record.cpp / ability_connect_manager.cpp
{
AbilityConfig::SYSTEM_UI_BUNDLE_NAME,
AbilityConfig::SYSTEM_UI_ABILITY_NAME
},
ServiceExtension 生命周期
AMS 发起 startServiceExtensionAbility(want)
│
▼
onCreate(want) ← SystemUI 在此回调中完成全部初始化
│ (initSystemUi + 创建系统窗口 + 注册上下文)
│ 不重写 onStart 回调
▼
[常驻运行] ← AMS 保活,异常自动重启
│
▼
onStop() ← 系统关机或主动销毁
6.3 initSystemUi 与普通应用初始化的区别
initSystemUi 做了什么
InitSystemUi.ts 代码仅做3件事:
export default function initSystemUi(context: ServiceExtensionContext): void {
EventManager.setContext(context); // ① 全局事件总线注入 Context
ScreenLockManager.init().then(() => {}); // ② 订阅屏幕亮灭事件
TimeManager.init(context); // ③ 初始化时间/时区管理
}
但这里的"初始化"不是创建 UI,而是搭建 SystemUI 的全局基础设施:
| 初始化项 | 实际工作 |
|---|---|
EventManager.setContext |
让 EventManager 持有 ServiceExtensionContext,使其能通过 startAbility() 启动其他 Ability(事件总线的 ability target) |
ScreenLockManager.init |
调用 commonEvent.createSubscriber() 订阅 COMMON_EVENT_SCREEN_OFF/ON,屏幕状态变化时通过 EventBus 发布 screenChangeEvent |
TimeManager.init |
初始化时间格式化与时区监听,为时钟组件提供实时更新时间 |
真正的"界面初始化"不在 initSystemUi 中,而是在各 Product Module 各自的 ServiceExtAbility.onCreate() 里执行的,例如 statusbar:
// product/phone/statusbar/src/main/ets/ServiceExtAbility/ServiceExtAbility.ts
async onCreate(want: Want): Promise<void> {
// ① 注册 Ability 上下文
AbilityManager.setContext(AbilityManager.ABILITY_NAME_STATUS_BAR, this.context);
// ② 注册显示变化监听(旋转时重建窗口)
display.on("change", (id) => { this.createNewWindow(false); });
// ③ 读取配置 → 计算位置 → 创建系统窗口
this.createNewWindow(true);
}
async createNewWindow(isNewWindow: boolean) {
let configInfo = await StatusBarConfiguration.getConfiguration();
// ... 计算窗口大小和位置(根据设备方向、屏幕比例)
if (isNewWindow) {
WindowManager.createWindow(
this.context,
WindowType.STATUS_BAR, // ← 创建系统窗口
{ left, top, width, height },
'pages/index' // ← 加载 UI 页面
).then(() => WindowManager.showWindow(WindowType.STATUS_BAR));
}
}
与普通应用初始化的对比
| 维度 | SystemUI | 普通应用 |
|---|---|---|
| 组件类型 | ServiceExtension(后台服务) |
UIAbility(带 UI 的前台组件) |
| 入口回调 | ServiceExtAbility.onCreate() |
UIAbility.onWindowStageCreate() |
| 窗口创建 | 手动调用 WindowManager.createWindow() 创建系统窗口,需指定类型、位置、大小 |
系统自动创建 TYPE_APP 窗口,通过 windowStage.loadContent() 加载 UI |
| 窗口类型 | TYPE_STATUS_BAR、TYPE_NAVIGATION_BAR 等系统级窗口 |
TYPE_APP 普通应用窗口 |
| 窗口数量 | 多个窗口(状态栏、导航栏、音量面板等各自独立) | 通常一个主窗口 |
| 初始化时机 | 开机时由 AMS 自动拉起 | 用户点击图标或被其他 Ability 请求 |
| 生命周期 | 常驻后台,AMS 保活+崩溃重启 | 按用户操作创建/销毁,系统可回收 |
| UI 框架 | 通过 Window.loadContent() 加载 ArkUI 页面 |
通过 WindowStage.loadContent() 加载 ArkUI 页面 |
| 上下文获取 | ServiceExtensionContext(无 UI 能力) |
UIAbilityContext(含 UI 能力) |
| 输入事件 | 系统窗口类型决定触控行为:状态栏/导航栏拦截交互相应手势(滑下通知栏等),部分覆盖层(音量面板等)可设置为穿透 | 普通窗口默认拦截所有触控 |
启动序列总览
系统开机
│
▼
AbilityManagerService.OnStart()
├── 启动 entry/phone ServiceExtAbility
│ └── onCreate()
│ ├── initSystemUi()
│ │ ├── EventManager.setContext()
│ │ ├── ScreenLockManager.init()
│ │ └── TimeManager.init()
│ └── AbilityManager.setContext(ENTRY)
│
├── 启动 product/phone/statusbar ServiceExtAbility
│ └── onCreate()
│ ├── AbilityManager.setContext(STATUS_BAR)
│ ├── display.on("change")
│ └── WindowManager.createWindow(TYPE_STATUS_BAR)
│
├── 启动 product/phone/navigationBar ServiceExtAbility
│ ├── AbilityManager.setContext(NAVIGATION_BAR)
│ └── WindowManager.createWindow(TYPE_NAVIGATION_BAR)
│
├── 启动 product/phone/dropdownpanel ServiceExtAbility
│ └── WindowManager.createWindow(TYPE_PANEL)
│
└── ...其他 product module 启动
7. 窗口类型映射与 Z-Order 机制
7.1 窗口类型概览
OHOS 通过 WindowType 枚举定义所有窗口的层叠顺序,API 暴露以下类型(从 JS Native API 映射到 C++ WindowSession 类型):
| API WindowType | C++ WindowType | 用途 |
|---|---|---|
TYPE_APP |
WINDOW_TYPE_APP_SUB_WINDOW |
普通应用窗口 |
TYPE_SYSTEM_ALERT |
WINDOW_TYPE_SYSTEM_ALARM_WINDOW |
系统警告 |
TYPE_INPUT_METHOD |
WINDOW_TYPE_INPUT_METHOD_FLOAT |
输入法 |
TYPE_STATUS_BAR |
WINDOW_TYPE_STATUS_BAR |
状态栏 |
TYPE_PANEL |
WINDOW_TYPE_PANEL |
面板/下拉面板 |
TYPE_KEYGUARD |
WINDOW_TYPE_KEYGUARD |
锁屏 |
TYPE_VOLUME_OVERLAY |
WINDOW_TYPE_VOLUME_OVERLAY |
音量/通知/控制覆盖层 |
TYPE_NAVIGATION_BAR |
WINDOW_TYPE_NAVIGATION_BAR |
导航栏 |
TYPE_FLOAT |
WINDOW_TYPE_FLOAT |
悬浮窗 |
TYPE_WALLPAPER |
WINDOW_TYPE_WALLPAPER |
壁纸 |
TYPE_DESKTOP |
WINDOW_TYPE_DESKTOP |
桌面/Launcher |
TYPE_DIVIDER |
WINDOW_TYPE_DOCK_SLICE |
分屏条 |
TYPE_SYSTEM_TOAST |
WINDOW_TYPE_SYSTEM_TOAST |
系统 Toast |
7.2 SystemUI 窗口映射
WindowManager 将逻辑窗口名称映射到底层 WindowType,包含 Z-Order 层级:
| SystemUI 组件 | 底层 WindowType | 用途 | Z 层级 |
|---|---|---|---|
SystemUi_StatusBar |
TYPE_STATUS_BAR |
状态栏 | 3(应用之上) |
SystemUi_NavigationBar |
TYPE_NAVIGATION_BAR |
导航栏 | 7(音量面板之上) |
SystemUi_DropdownPanel |
TYPE_PANEL |
下拉面板 | 4(状态栏之上) |
SystemUi_NotificationPanel |
TYPE_VOLUME_OVERLAY |
通知面板 | 6 |
SystemUi_ControlPanel |
TYPE_VOLUME_OVERLAY |
控制面板 | 6 |
SystemUi_VolumePanel |
TYPE_VOLUME_OVERLAY |
音量面板 | 6 |
SystemUi_BannerNotice |
TYPE_VOLUME_OVERLAY |
Banner 通知 | 6 |
SystemUi_SplitBar |
TYPE_DIVIDER |
分屏条 | 20(高优先级) |
SystemUi_PrivacyIndicator |
TYPE_VOLUME_OVERLAY |
隐私指示器 | 6 |
7.3 Z-Order 层叠原理
ApiWindowType 枚举的值顺序直接编码 Z 轴层级,值越大越靠上:
Z-Order 从低到高
─────────────────────────────────────────────
TYPE_APP (0) 普通应用窗口 [底层]
TYPE_SYSTEM_ALERT(1) 系统警告
TYPE_INPUT_METHOD(2) 输入法软键盘
TYPE_STATUS_BAR(3) 状态栏 ← SystemUI
TYPE_PANEL(4) 下拉面板 ← SystemUI
TYPE_KEYGUARD(5) 锁屏
TYPE_VOLUME_OVERLAY(6) 音量面板 ← SystemUI
TYPE_NAVIGATION_BAR(7) 导航栏 ← SystemUI
TYPE_FLOAT(8) 悬浮窗
TYPE_WALLPAPER(9) 壁纸
TYPE_DESKTOP(10) 桌面/Launcher
TYPE_LAUNCHER_RECENT(11) 最近任务
TYPE_LAUNCHER_DOCK(12) Dock 栏
...
TYPE_SYSTEM_TOAST(18) 系统 Toast
TYPE_DIVIDER(20) 分屏条 ← SystemUI
…
TYPE_FB(28) 开机第一屏
─────────────────────────────────────────────
渲染效果示意:
┌─────────────────────────────────────────┐
│ TYPE_PANEL 下拉控制中心/通知 │ ← 最上层(下拉时可见)
├─────────────────────────────────────────┤
│ TYPE_STATUS_BAR 状态栏 (时间/电量) │ ← 常驻显示
├─────────────────────────────────────────┤
│ TYPE_APP 普通应用界面 │ ← 中间
├─────────────────────────────────────────┤
│ TYPE_WALLPAPER 壁纸 │ ← 底层
└─────────────────────────────────────────┘
7.4 为什么普通应用做不到同样效果
| 机制 | 限制说明 |
|---|---|
| 系统签名 | TYPE_STATUS_BAR、TYPE_NAVIGATION_BAR 等只允许系统签名应用创建 |
| 权限控制 | 普通应用调用 Window.create() 传系统类型会返回权限拒绝 |
| APL 等级 | 需要 system_core 或 system_basic 等级才能创建系统窗口 |
| Z-Order 固定 | 普通应用只能使用 TYPE_APP 或 TYPE_FLOAT,无法自选层级 |
7.5 窗口创建流程
// SystemUI 创建系统窗口(statusbar 为例)
import Window from '@ohos.window';
let winHandle = await Window.create(
context,
'SystemUi_StatusBar', // 窗口名称
Window.WindowType.TYPE_STATUS_BAR // ← 关键:系统窗口类型
);
await winHandle.moveTo(0, 0);
await winHandle.resetSize(width, height);
await winHandle.loadContent('pages/StatusBarComponent');
await winHandle.show();
服务端流程:
SystemUI createWindow(TYPE_STATUS_BAR)
│
▼
WindowManagerService (C++ 层)
├─ 验证调用方是否有系统签名权限
├─ 创建 WindowNode(windowType = TYPE_STATUS_BAR)
├─ 计算 zOrder(由 WindowType 推算)
├─ 注册到 WindowRoot(全局窗口树)
└─ 通知渲染服务 Rosen/RenderService
│
▼
RenderService(GPU 合成器)
├─ 按 zOrder 排序所有窗口
└─ 按序合成帧 → 输出到屏幕
7.6 普通应用的窗口区域与沉浸式配置
为何普通应用只渲染中间部分
普通应用只能使用 TYPE_APP 窗口类型,其窗口区域默认被限制在安全区域内——即状态栏与导航栏之间的可用区域。这是因为 OHOS 的 WindowManagerService 默认对所有 TYPE_APP 窗口应用 AvoidArea(避让区域):
AvoidAreaType::TYPE_SYSTEM— 状态栏和导航栏占用的区域AvoidAreaType::TYPE_CUTOUT— 屏幕挖孔/刘海区域AvoidAreaType::TYPE_SYSTEM_GESTURE— 手势导航区域AvoidAreaType::TYPE_NAVIGATION_INDICATOR— 导航指示条区域AvoidAreaType::TYPE_KEYBOARD— 键盘弹出区域
默认布局效果:
┌──────────────────────────────────────┐
│ STATUS BAR (TYPE_STATUS_BAR) Z=3 │ ← 系统窗口,常驻
├──────────────────────────────────────┤
│ ← AvoidArea TYPE_SYSTEM (状态栏) → │
├──────────────────────────────────────┤
│ │
│ 普通应用窗口 (TYPE_APP) Z=0 │ ← 应用窗口默认大小被限制在此
│ │
│ │
├──────────────────────────────────────┤
│ ← AvoidArea TYPE_SYSTEM (导航栏) → │
├──────────────────────────────────────┤
│ NAVIGATION BAR (TYPE_NAVIGATION_BAR) │ ← 系统窗口,常驻
└──────────────────────────────────────┘
沉浸式配置(扩展渲染到状态栏/导航栏区域)
应用可以通过以下 API 实现沉浸式效果,让背景延展到状态栏和导航栏下方:
方法一:setWindowLayoutFullScreen(true)
import { Window } from '@kit.ArkUI';
// 在 UIAbility 的 onWindowStageCreate 中
onWindowStageCreate(windowStage: window.WindowStage): void {
// 1. 加载主窗口
windowStage.loadContent('pages/Index', (err, data) => {
// 2. 获取主窗口
let windowClass = windowStage.getMainWindowSync();
// 3. 设置为全屏布局(背景可延展到状态栏和导航栏下方)
windowClass.setWindowLayoutFullScreen(true)
.then(() => {
console.log('Full screen layout enabled');
});
// 4. (可选) 隐藏系统状态栏和导航栏
windowClass.setWindowSystemBarEnable([])
.then(() => {
console.log('System bars hidden');
});
});
}
方法二:setWindowSystemBarProperties() 设置透明状态栏
// 仅让状态栏/导航栏透明,背景透过去,但保留系统栏本身
windowClass.setWindowSystemBarProperties({
statusBarColor: '#00000000', // 状态栏全透明
statusBarContentColor: '#FFFFFF', // 状态栏文字/图标白色(适配深色背景)
navigationBarColor: '#00000000', // 导航栏全透明
navigationBarContentColor: '#FFFFFF', // 导航栏图标白色
}).then(() => {
console.log('System bar properties set');
});
方法三:ArkUI 组件级适配(expandSafeArea)
ArkUI 提供声明式 API 控制单个组件的安全区域扩展:
@Entry
@Component
struct ImmersivePage {
build() {
Column() {
// 内容区域
Text('这是状态栏下方的文字')
}
.width('100%')
.height('100%')
// 扩展安全区域到顶部(状态栏下方)和底部(导航栏上方)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
// 背景颜色会延展到状态栏和导航栏下方
.backgroundColor('#FF6200EE')
}
}
或者通过 backgroundColor 配合 expandSafeArea 实现"安全区域"的视觉延展:
// 顶部状态栏区域使用组件背景色填充
Stack() {
Column() { /* 主内容 */ }
.width('100%')
.height('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
.backgroundColor('#FF6200EE')
方法四:获取避让区域手动适配
// 获取系统避让区域(状态栏、导航栏的实际位置和大小)
let avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
// avoidArea.topRect → 状态栏区域
// avoidArea.bottomRect → 导航栏区域
// avoidArea.leftRect → 左避让
// avoidArea.rightRect → 右避让
// 根据避让区域坐标手动调整 UI 布局
配置效果示意
默认状态(无沉浸式):
┌──────────────────────────────┐
│ Status Bar (黑底白字) │
├──────────────────────────────┤
│ │
│ 应用内容(不进入状态栏区域) │
│ │
├──────────────────────────────┤
│ Navigation Bar (黑底白图) │
└──────────────────────────────┘
沉浸式(setWindowLayoutFullScreen + setWindowSystemBarEnable透明):
┌──────────────────────────────┐
│ ← 应用背景延伸到状态栏下方 → │ ← 状态栏文字图标仍可见
├──────────────────────────────┤
│ │
│ 应用内容 │
│ │
├──────────────────────────────┤
│ ← 应用背景延伸到导航栏下方 → │ ← 导航栏图标仍可见
└──────────────────────────────┘
AvoidAreaType 枚举
| 类型 | 说明 | 来自 |
|---|---|---|
TYPE_SYSTEM |
状态栏和导航栏占用的避让区域 | SystemUI 创建的窗口 |
TYPE_CUTOUT |
屏幕挖孔/刘海区域 | 硬件 |
TYPE_SYSTEM_GESTURE |
手势导航区域 | 系统 |
TYPE_KEYBOARD |
键盘弹出时的避让区域 | 输入法窗口 |
TYPE_NAVIGATION_INDICATOR |
导航指示条区域 | 系统 |
对比 Android
| 维度 | OHOS | Android |
|---|---|---|
| 全屏布局 | setWindowLayoutFullScreen(true) |
View.setSystemUiVisibility() / WindowCompat.setDecorFitsSystemWindows() |
| 系统栏透明 | setWindowSystemBarProperties({statusBarColor: '#00000000'}) |
window.statusBarColor = Color.TRANSPARENT |
| 安全区域 | getWindowAvoidArea() / expandSafeArea() |
WindowInsetsCompat.getInsets(SystemBars) |
| 导航手势避让 | AvoidAreaType.TYPE_SYSTEM_GESTURE |
WindowInsetsCompat.Type.systemGestures() |
| 声明式适配 | ArkUI expandSafeArea 属性 |
Jetpack Compose WindowInsets / Modifier.statusBarsPadding() |
8. 事件总线设计
8.1 整体架构(5 层模型)
SystemUI 的事件体系分为 5 层:
@ohos.commonEvent (系统级公共事件)
│
▼
CommonEventManager (订阅系统事件 → 转为内部事件)
│
▼
EventManager (统一分发: local / ability / commonEvent / remote 四种 target)
│
▼
EventBus (进程内发布-订阅,feature 组件消费)
│
▼
StatusBarService / ControlCenterService / NotificationViewModel (UI 更新)
8.2 事件类型与分发机制
EventManager 支持 4 种 target:
| Target | 触发行为 | 当前状态 |
|---|---|---|
local |
EventBus.emit() 进程内事件分发 |
✅ 已实现 |
ability |
context.startAbility() 启动指定 Ability |
✅ 已实现 |
commonEvent |
发布公共事件给其他应用 | ❌ 占位(返回 false) |
remote |
跨设备事件分发 | ❌ 占位(返回 false) |
8.3 事件定义与路由
事件通过 EventUtil 的工具函数构建:
// Event 结构
type Event = {
target: EventTarget; // "local" | "ability" | "commonEvent" | "remote"
data: { [key: string]: any };
};
本地事件(local):进程内通知,由 EventBus 的发布-订阅机制承载。
// 构建本地事件
export function obtainLocalEvent(event: string, args: any): Event {
return { target: "local", data: { eventName: event, args } };
}
// 发布
EventManager.publish(obtainLocalEvent('screenChangeEvent', true));
// 订阅
EventManager.subscribe('screenChangeEvent', (isScreenOn: boolean) => { ... });
Ability 事件(ability):通过 context.startAbility() 跨 Ability 通信。
// 构建 Ability 事件
export function obtainStartAbility(bundleName: string, abilityName: string, args?: any): Event {
return { target: "ability", data: { bundleName, abilityName, args } };
}
8.4 EventBus 的实现
EventBus 采用泛型发布-订阅模式,支持按事件名管理回调集合:
export function createEventBus<T extends string>(): EventBus<T> {
let _cbs: { [key: string]: Set<Callback> } = {};
function on(events: T | T[], cb: Callback): () => void { ... } // 订阅(可批量)
function once(event: T, cb: Callback): () => void { ... } // 一次性订阅
function off(event: T | T[] | undefined, cb: Callback): void {} // 取消订阅
function emit(event: T, args: any): void { ... } // 发布
}
关键特性:
on()返回退订函数(unsubscribe),支持自动清理- 支持批量订阅:
EventBus.on(['eventA', 'eventB'], callback) EventManager.subscribe()是对EventBus.on()的封装
8.5 事件流实例
系统中有以下已注册的事件流:
| 事件名称 | 发布者 | 订阅者 | 触发条件 |
|---|---|---|---|
screenChangeEvent |
ScreenLockManager |
CommonEventManager(ScreenPolicy) |
屏幕亮/灭 |
Time_Change_Event |
TimeManager |
时钟组件 | 时间变化/时区变更/分钟跳变 |
WindowShowHideEvent |
WindowManager |
各窗口组件 | 窗口显示/隐藏 |
WindowResizeEvent |
WindowManager |
各窗口组件 | 窗口尺寸变化 |
WindowDestroy |
WindowManager |
各窗口组件 | 窗口销毁 |
startAbilityEvent |
EventManager | — | 通过 Ability 事件启动 Ability(内部通知,无外部订阅者) |
onBannerNoticeShow |
NotificationViewModel |
Banner 通知组件 | Banner 通知显示 |
以 时间更新 事件流为例,展示完整链路:
@ohos.commonEvent
├─ COMMON_EVENT_TIME_CHANGED }
├─ COMMON_EVENT_TIMEZONE_CHANGED }── 系统触发
└─ COMMON_EVENT_TIME_TICK }
│
▼
CommonEventManager (TimeManager.init() 中注册)
│ 收到系统公共事件
▼
TimeManager.notifyTimeChange()
│ 组装 TimeEventArgs { date, timeFormat }
▼
EventManager.publish(obtainLocalEvent(TIME_CHANGE_EVENT, args))
│
▼
EventBus.emit('Time_Change_Event', args)
│
▼
时钟组件的 ViewModel(订阅 Time_Change_Event)
│ 更新 UI 绑定的时间文本
▼
ArkUI 界面刷新(显示最新时间)
8.6 事件字符串协议
StatusBarService 和 ControlCenterService 中的插件数据源(PluginComponentData)通过字符串格式描述事件触发行为:
格式: "local=eventName|args" 或 "ability=bundleName|abilityName"
export function parseEventString(eventString: string | undefined): Event | undefined {
let [eventType, eventData] = eventString.split("=");
if (eventType == LOCAL_EVENT_TYPE && eventData) {
let [localEventName, args] = eventData.split("|");
return obtainLocalEvent(localEventName, args);
}
if (eventType == START_ABILITY_TYPE && eventData) {
let [bundleName, abilityName] = eventData.split("|");
return obtainStartAbility(bundleName, abilityName);
}
}
示例:
- 状态栏插件声明点击跳转:
"ability=com.ohos.settings|com.ohos.settings.MainAbility" - 控制中心 Toggle 触发本地事件:
"local=toggle_click|wifi"
这是 SystemUI 插件扩展机制(见 4.3 节)的运行时通信协议——第三方插件注册时通过 metadata 声明事件字符串,SystemUI 解析后触发对应行为。
8.7 与 Android 对比
| 维度 | OHOS SystemUI | Android |
|---|---|---|
| 进程内事件 | EventBus (内部 pub/sub) | LocalBroadcastManager / LiveData / Flow |
| 跨 Ability 事件 | EventManager ability target → startAbility() | Intent / startActivity() |
| 系统级事件 | @ohos.commonEvent | BroadcastReceiver |
| 插件事件协议 | parseEventString("local=/ability=...") | IntentFilter / PendingIntent |
| 事件-UI 绑定 | ViewModel 订阅 EventBus, 通知 ArkUI 状态更新 | ViewModel + LiveData/Flow, 通知 Compose/View |
9. 多设备适配
SystemUI 为手机(phone)和 PC 两种设备形态提供差异化的 UI 和功能配置,核心通过 product/ 目录结构和 entry/ 入口分离实现。
9.1 三层次产品架构
systemui/product/
├── default/ ← 跨设备共享模块(所有形态通用)
│ ├── dialog/ # 系统对话框
│ ├── navigationBar/ # 导航栏(三按钮/手势)
│ ├── notificationmanagement/# 通知管理设置
│ └── volumepanel/ # 音量面板
├── phone/ ← 手机设备专属
│ ├── statusbar/ # 手机状态栏
│ └── dropdownpanel/ # 下拉控制中心(手机特有)
└── pc/ ← PC 设备专属
├── statusbar/ # PC 状态栏(布局不同)
├── controlpanel/ # PC 控制面板
└── notificationpanel/ # PC 通知面板
systemui/entry/
├── phone/ ← 手机入口
└── pc/ ← PC 入口
9.2 模块分配
| 模块 | 设备分布 | 说明 |
|---|---|---|
statusbar |
phone + pc(各一份独立实现) | 状态栏布局在手机和 PC 上差异大(手机显示时间/信号/电量,PC 显示系统托盘/通知图标) |
navigationBar |
default 共享 | 导航栏(三按键或手势指示器)跨设备通用 |
dialog |
default 共享 | 系统弹窗 UI 一致 |
volumepanel |
default 共享 | 音量控制面板通用 |
notificationmanagement |
default 共享 | 通知管理设置页面通用 |
dropdownpanel |
phone 独有 | 下拉控制中心(手机典型交互) |
controlpanel |
pc 独有 | PC 端的系统控制面板 |
notificationpanel |
pc 独有 | PC 端的通知中心(独立于控制面板) |
9.3 差异化策略
共享模块(product/default/):所有设备类型使用同一份源码,通过 module.json5 中的 deviceTypes 字段声明支持的设备类型:
{
"module": {
"name": "default_navigationBar",
"type": "feature",
"deviceTypes": ["default", "tablet"] // 支持手机和平板
}
}
设备专属模块(product/phone/、product/pc/):不同设备使用独立的 ServiceExtAbility 和页面配置,在 build-profile.json5 中通过编译条件选择:
// phone_dropdownpanel (phone 独有)
{ "name": "phone_dropdownpanel", "type": "feature", "srcEntrance": "./ets/Application/AbilityStage.ts" }
// pc_controlpanel (pc 独有)
{ "name": "pc_controlpanel", "type": "feature", "srcEntrance": "./ets/Application/AbilityStage.ts" }
入口分离(entry/):entry/phone/ 和 entry/pc/ 各自打包为独立的 entry HAP,在产品构建时根据 productdefine/ 中的产品定义选择对应的 entry。
9.4 产品构建选择
构建时通过 GN 或 hvigor 选择产品配置,最终 HAP 包仅包含目标设备类型的模块:
phone HAP:
entry/phone/ + product/phone/ + product/default/ + features/
pc HAP:
entry/pc/ + product/pc/ + product/default/ + features/
9.5 运行时差异化
除了编译时的模块选择,SystemUI 在运行时也通过 deviceInfo.deviceType 做差异化逻辑:
// product/phone/statusbar ServiceExtAbility.ts
import deviceInfo from '@ohos.deviceInfo';
const deviceTypeInfo = deviceInfo.deviceType;
// ...
if (deviceTypeInfo === 'phone') {
screenFactor = this.direction === 1 ? 1188 : 540; // 手机屏占比计算
} else {
screenFactor = this.direction === 1 ? 640 : 360; // 非手机设备
}
features/ 中的组件通过 moduleName 参数区分所属的产品模块,从而实现同一份 feature 源码为不同设备提供差异化功能。
9.6 与 Android 对比
| 维度 | OHOS SystemUI | Android SystemUI |
|---|---|---|
| 多设备策略 | product/ 目录区分 + 编译时选择 | 通用组件 + 运行时 overlay(overlay/ 目录) |
| 设备类型感知 | deviceInfo.deviceType 运行时 |
Build.TAGS / PackageManager.hasSystemFeature() |
| 共享模块 | product/default/ |
base AOSP 源码 |
| 差异化模块 | product/phone/ vs product/pc/ |
vendor overlay / product overlay |
| 形态适配粒度和入口分离 | 模块级 + entry 级分流 | 同 APK,运行时判断 |
| 构建选择 | hvigor/GN 区分产品定义 | Soong / product makefile 选择 overlay |

浙公网安备 33010602011771号