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 类窗口的创建、移动、显示、隐藏、销毁;映射 WindowTypeWindow.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_BARTYPE_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_BARTYPE_NAVIGATION_BAR 等只允许系统签名应用创建
权限控制 普通应用调用 Window.create() 传系统类型会返回权限拒绝
APL 等级 需要 system_coresystem_basic 等级才能创建系统窗口
Z-Order 固定 普通应用只能使用 TYPE_APPTYPE_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 事件字符串协议

StatusBarServiceControlCenterService 中的插件数据源(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
posted @ 2026-05-27 16:46  getmoon  阅读(42)  评论(0)    收藏  举报