鸿蒙新特性实战:@ohos.commonEventManager — 公共事件发布订阅与系统事件总线
引言
在移动应用架构中,组件间的通信始终是一个核心命题。HarmonyOS 的 Stage 模型将 UI 页面与后台 Ability 隔离在不同生命周期中,客观上要求一套可靠的跨组件、跨进程通信机制。Android 开发者熟悉 BroadcastReceiver,iOS 开发者熟悉 NotificationCenter——而 HarmonyOS NEXT 的答案是 @ohos.commonEventManager,一套完整的公共事件(Common Event)发布/订阅系统。
与 @ohos.events.emitter 的进程内事件总线不同,commonEventManager 工作在系统服务层——事件经由系统 Common Event Service 路由,可以实现跨应用、跨进程的事件传递。系统本身也通过这套机制广播电池状态、网络连接、时区变化等底层状态事件(即 Support 枚举中定义的各类系统公共事件)。
本文将构建一个事件总线实验室 Demo,从零开始演示自定义事件的完整生命周期:创建订阅者(Subscriber)、注册事件监听、发布带数据的公共事件、处理有序事件(Ordered Event),并以实时日志追踪每一步的状态流转。
读完本文,你将掌握:
- 订阅者创建:
createSubscriberSync()— 同步构建 CommonEventSubscriber - 事件订阅:
subscribe()— 注册带事件匹配规则的持久监听 - 事件发布:
publish()— 发布基本事件 / 带 code + data 的结构化事件 - 有序事件:
isOrdered— 有序事件的发布与结果码处理 - 数据流:
CommonEventPublishData→CommonEventData— 发布端到接收端的数据传递 - 系统事件:
Support枚举 — 30+ 系统公共事件的分类与用途
环境与权限
@ohos.commonEventManager 属于 BasicServicesKit 工具包,自 API 9 开始提供。导入方式:
import { commonEventManager } from '@kit.BasicServicesKit';
重要发现:发布和订阅自定义事件不需要任何权限。只有以下操作需要额外权限:
| 操作 | 所需权限 | 级别 |
|---|---|---|
| 发布自定义事件 | 无需权限 | — |
| 订阅自定义事件 | 无需权限 | — |
| 发布粘性事件 (isSticky) | ohos.permission.COMMONEVENT_STICKY |
system_grant |
| 订阅部分系统事件 | 视具体事件而定 | 部分需 system 权限 |
对于大多数应用场景(应用内跨组件通信、自定义事件总线),commonEventManager 是零权限开销的。本文 Demo 也无需任何权限声明即可运行。
一、核心数据结构
在深入 API 使用之前,先理解四个关键接口。
1.1 CommonEventSubscribeInfo — 订阅配置
interface CommonEventSubscribeInfo {
events: Array<string>; // 要订阅的事件名列表(支持多事件匹配)
publisherPermission?: string; // 发布者所需权限(过滤来源)
publisherDeviceId?: string; // 发布者设备 ID(分布式场景)
userId?: number; // 发布者用户 ID
priority?: number; // 订阅者优先级(-100 ~ +1000)
publisherBundleName?: string; // 发布者 Bundle Name(精确定向)
}
events 是唯一必填字段,支持字符串数组——一个 Subscriber 可以同时匹配多个事件名。priority 在有序事件中至关重要:高优先级的订阅者先收到事件,并有机会通过 setCode() / setData() 影响下游订阅者。
匹配规则:
- 事件名精确匹配时,订阅者收到事件
- 若指定了
publisherBundleName,仅接收该 Bundle 发布的事件 - 若指定了
publisherPermission,仅接收声明了该权限的发布者的事件
1.2 CommonEventPublishData — 发布数据
interface CommonEventPublishData {
bundleName?: string; // 限定接收者的 Bundle Name
code?: number; // 结果码(默认 0)
data?: string; // 字符串数据(最大 64KB)
subscriberPermissions?: Array<string>; // 接收者所需权限
isOrdered?: boolean; // 是否有序事件(默认 false)
isSticky?: boolean; // 是否粘性事件(需 COMMONEVENT_STICKY 权限)
parameters?: { [key: string]: any }; // 附加键值对
}
code 和 data 是发布者向订阅者传递信息的两个主通道。code 通常用于状态码(类似 HTTP 状态码),data 用于承载字符串数据。parameters 提供了更灵活的键值对扩展——可以传递任意 JSON 可序列化的结构。
注意:data 字段限制 64KB,如果需要传输更大的数据,应通过文件路径或 URI 传递。
1.3 CommonEventData — 接收数据
interface CommonEventData {
event: string; // 事件名称
bundleName?: string; // 发布者的 Bundle Name
code?: number; // 发布时传入的 code
data?: string; // 发布时传入的 data
parameters?: { [key: string]: any }; // 发布时传入的 parameters
}
订阅者在回调中收到 CommonEventData,它是 CommonEventPublishData 的"镜像"——发布时的 code、data、parameters 原封不动传递到接收端。event 字段告诉订阅者当前触发的是哪个事件(当订阅了多个事件时尤为重要)。
1.4 CommonEventSubscriber — 订阅者实例
interface CommonEventSubscriber {
getCode(callback: AsyncCallback<number>): void;
getCodeSync(): number; // 同步获取当前结果码
setCode(code: number): Promise<void>;
setCodeSync(code: number): void; // 同步设置结果码
getData(callback: AsyncCallback<string>): void;
getDataSync(): string; // 同步获取当前结果数据
setData(data: string): Promise<void>;
setCodeAndData(code: number, data: string): Promise<void>;
isOrderedCommonEventSync(): boolean; // 是否有序事件
isStickyCommonEventSync(): boolean; // 是否粘性事件
abortCommonEventSync(): void; // 中断有序事件传递
finishCommonEvent(): Promise<void>; // 完成有序事件处理
getSubscribeInfoSync(): CommonEventSubscribeInfo; // 获取订阅信息
}
CommonEventSubscriber 不仅是订阅句柄,在有序事件中,它还是事件处理的控制点。通过 setCode() / setData(),订阅者可以修改传递给下一个订阅者的结果;通过 abortCommonEventSync(),可以终止事件继续向后传递。
二、完整生命周期
一个完整的自定义事件流包含五个步骤:
2.1 创建订阅者
import { commonEventManager } from '@kit.BasicServicesKit';
// 定义订阅信息
let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
events: ['com.example.MY_EVENT']
};
// 同步创建 Subscriber(API 10+)
let subscriber = commonEventManager.createSubscriberSync(subscribeInfo);
createSubscriberSync() 是 API 10 引入的同步方法,直接返回 CommonEventSubscriber 实例。如果你的项目最低 API 版本是 9,可以使用 Promise 版本:
let subscriber = await commonEventManager.createSubscriber(subscribeInfo);
CommonEventSubscribeInfo 的 events 数组支持多个事件名——一个 Subscriber 可以同时监听多个不同的事件。这比 Android BroadcastReceiver 的 IntentFilter 设计更简洁。
2.2 订阅事件
commonEventManager.subscribe(subscriber, (err: Error, data: commonEventManager.CommonEventData) => {
if (err) {
console.error('订阅失败: ' + JSON.stringify(err));
return;
}
console.log('收到事件: ' + data.event);
console.log(' code: ' + (data.code || 0));
console.log(' data: ' + (data.data || ''));
console.log(' bundleName: ' + (data.bundleName || '(本应用)'));
});
subscribe() 注册一个异步回调,当匹配的事件被发布时,系统会调用该回调并传入 CommonEventData。回调在主线程执行——如果处理逻辑较重,应考虑异步化。
2.3 发布事件
// 基本发布:仅事件名
commonEventManager.publish('com.example.MY_EVENT', (err: Error) => {
if (err) {
console.error('发布失败: ' + JSON.stringify(err));
} else {
console.log('发布成功');
}
});
// 带数据发布
let publishData: commonEventManager.CommonEventPublishData = {
code: 200,
data: '{"msg":"Hello CommonEvent!"}',
isOrdered: false
};
commonEventManager.publish('com.example.MY_EVENT', publishData, (err: Error) => {
// ...
});
publish() 是异步的——事件被提交到系统 Common Event Service 后,由 Service 异步分发给所有匹配的订阅者。发布者不等待订阅者处理完毕。
2.4 取消订阅
commonEventManager.unsubscribe(subscriber);
unsubscribe() 传入 Subscriber 实例,系统会移除该订阅者的所有事件监听。未取消的订阅可能导致内存泄漏——建议在页面 aboutToDisappear() 生命周期中清理。
2.5 完整示例:计数器事件广播
import { commonEventManager } from '@kit.BasicServicesKit';
// 发布端
let publishInfo: commonEventManager.CommonEventPublishData = {
code: 1,
data: 'increment'
};
commonEventManager.publish('com.example.COUNTER', publishInfo, (err: Error) => {
console.log('计数器事件已发布');
});
// 接收端
let info: commonEventManager.CommonEventSubscribeInfo = {
events: ['com.example.COUNTER']
};
let sub = commonEventManager.createSubscriberSync(info);
commonEventManager.subscribe(sub, (err: Error, data: commonEventManager.CommonEventData) => {
if (data.code === 1 && data.data === 'increment') {
// 执行计数器加一操作
}
});


三、有序事件(Ordered Event)
有序事件是 commonEventManager 的独特设计之一。当 isOrdered 设为 true 时,事件按订阅者优先级串行传递——高优先级订阅者先收到,可以修改结果码/数据,甚至可以中断传递链。
3.1 工作原理
发布者 publish(isOrdered=true)
│
▼
订阅者 A (priority=1000) ← 最先收到
│ setCodeAndData(1, 'processed_by_A')
▼
订阅者 B (priority=500) ← 收到 A 修改后的数据
│ getCodeSync() → 1
│ getDataSync() → 'processed_by_A'
│ abortCommonEventSync() ← 中断传递
▼
订阅者 C (priority=100) ← 不会收到(已被中断)
│
▼
发布者回调 → publish 完成
3.2 代码演示
// 发布者发布有序事件
let publishData: commonEventManager.CommonEventPublishData = {
code: 0,
data: 'original',
isOrdered: true // 关键:标记为有序事件
};
commonEventManager.publish('com.example.ORDERED', publishData, (err: Error) => {
console.log('有序事件发布完成');
});
// 高优先级订阅者也叫"A"
let subA = commonEventManager.createSubscriberSync({
events: ['com.example.ORDERED'],
priority: 1000
});
commonEventManager.subscribe(subA, (err: Error, data: commonEventManager.CommonEventData) => {
// 修改结果,传递给下游
subA.setCodeAndDataSync(1, 'processed_by_A');
subA.finishCommonEvent(); // 必须调用,否则事件链卡住
});
// 中等优先级订阅者"B"
let subB = commonEventManager.createSubscriberSync({
events: ['com.example.ORDERED'],
priority: 500
});
commonEventManager.subscribe(subB, (err: Error, data: commonEventManager.CommonEventData) => {
let code = subB.getCodeSync(); // → 1(由 A 设置)
let d = subB.getDataSync(); // → 'processed_by_A'
// 中断传递,C 不会收到此事件
subB.abortCommonEventSync();
subB.finishCommonEvent();
});
// 低优先级订阅者"C"
let subC = commonEventManager.createSubscriberSync({
events: ['com.example.ORDERED'],
priority: 100
});
commonEventManager.subscribe(subC, (err: Error, data: commonEventManager.CommonEventData) => {
// 当 A 或 B 调用了 abortCommonEventSync() 后,C 不会执行到这里
});
关键规则:
finishCommonEvent()是必需的——不调用会导致事件链永久阻塞,后续订阅者永远等不到abortCommonEventSync()中断传递,之后的订阅者不会收到事件- 优先级值域为 -100 ~ +1000,值越大越先收到
四、系统公共事件(Support 枚举)
Support 枚举定义了 30+ 系统公共事件,覆盖了设备管理的数个关键领域:
| 分类 | 事件名 | 触发时机 |
|---|---|---|
| 设备状态 | usual.event.BOOT_COMPLETED |
设备启动完成 |
| 设备状态 | usual.event.SHUTDOWN |
设备关机 |
| 电池 | usual.event.BATTERY_CHANGED |
电池状态变化 |
| 电池 | usual.event.BATTERY_LOW |
低电量 |
| 电池 | usual.event.BATTERY_OKAY |
电量恢复正常 |
| 电源 | usual.event.POWER_CONNECTED |
电源连接 |
| 电源 | usual.event.POWER_DISCONNECTED |
电源断开 |
| 屏幕 | usual.event.SCREEN_ON |
屏幕点亮 |
| 屏幕 | usual.event.SCREEN_OFF |
屏幕关闭 |
| 时间 | usual.event.TIME_TICK |
每分钟计时 |
| 时间 | usual.event.TIME_CHANGED |
系统时间被修改 |
| 时间 | usual.event.TIMEZONE_CHANGED |
时区变更 |
| 温控 | usual.event.THERMAL_LEVEL_CHANGED |
温度等级变化 |
| 应用 | usual.event.PACKAGE_ADDED |
新应用安装(系统应用) |
| 应用 | usual.event.PACKAGE_REPLACED |
应用更新(系统应用) |
注意:标有"系统应用"的事件需要 system 权限或系统应用身份才能订阅。普通应用可以自由订阅电池、电源、屏幕、时间、时区等无需权限的事件——这些是打造系统感知型应用的绝佳入口。
以下示例演示订阅电池变化事件:
let info: commonEventManager.CommonEventSubscribeInfo = {
events: [
commonEventManager.Support.COMMON_EVENT_BATTERY_CHANGED,
commonEventManager.Support.COMMON_EVENT_POWER_CONNECTED
]
};
let sub = commonEventManager.createSubscriberSync(info);
commonEventManager.subscribe(sub, (err: Error, data: commonEventManager.CommonEventData) => {
if (data.event === commonEventManager.Support.COMMON_EVENT_BATTERY_CHANGED) {
console.log('电池状态变化: code=' + data.code);
} else if (data.event === commonEventManager.Support.COMMON_EVENT_POWER_CONNECTED) {
console.log('电源已连接');
}
});
五、实战 Demo:事件总线实验室
页面结构
事件总线实验室
├── 状态栏 — 订阅状态 + 已收到事件计数
├── 事件订阅配置
│ ├── 事件名输入框
│ └── 订阅 / 取消订阅 按钮
├── 事件发布面板
│ ├── 事件名输入框
│ ├── Code 输入框
│ ├── Data 输入框
│ ├── 有序事件 Toggle
│ └── 发布按钮
├── 事件日志 — 最近 50 条(含时间戳)
├── 系统公共事件参考 — 10 个常用事件
└── 核心 API 参考 — 10 个关键 API
4 个交互点
- 订阅自定义事件 — 输入事件名,点击"订阅"创建 Subscriber 并注册回调,状态实时反馈
- 发布事件 — 编辑事件名、code、data,勾选有序/无序,点击"发布"触发;日志立即显示发布成功和接收确认
- 有序事件切换 — 勾选有序事件 Toggle 后发布,Demo 演示发布端如何通过
isOrdered参数控制事件传递模式 - 实时事件日志 — 所有操作(订阅/取消/发布/接收/异常)均以带时间戳的彩色日志呈现,最近 50 条
核心代码位置
完整代码在 dev/entry/src/main/ets/pages/EventBusLabPage.ets(约 330 行),路由已注册为 pages/EventBusLabPage。
六、commonEventManager vs events.emitter
HarmonyOS 提供了两个事件机制,理解它们的差异至关重要:
| 维度 | commonEventManager | @ohos.events.emitter |
|---|---|---|
| 通信范围 | 跨进程(系统级) | 进程内 |
| 底层机制 | 系统 Common Event Service | 内存事件表 |
| 权限需求 | 无(自定义事件) | 无 |
| 有序事件 | 支持(priority + abort) | 支持(EventPriority) |
| 粘性事件 | 支持(需权限) | 不支持 |
| 系统事件 | Support 枚举(30+) | 不支持 |
| 适用场景 | 跨应用通信、系统监听 | 页面/组件解耦 |
简而言之:跨进程通信用 commonEventManager,进程内解耦用 emitter。本文 Demo 虽然只在单应用内收发,但展示了完整的 commonEventManager API 栈,便于扩展到跨应用场景。
七、与 Android BroadcastReceiver 的对比
| 维度 | Android BroadcastReceiver | HarmonyOS commonEventManager |
|---|---|---|
| 注册方式 | 动态注册 + 静态注册(Manifest) | createSubscriberSync + subscribe |
| 传递数据 | Intent.putExtra() | code + data + parameters |
| 有序广播 | sendOrderedBroadcast() | isOrdered + priority + abort |
| 粘性广播 | sendStickyBroadcast() (Deprecated) | isSticky(需权限) |
| 系统广播 | Intent.ACTION_BATTERY_CHANGED 等 | Support 枚举 |
| 权限控制 | 自定义 permission | publisherPermission / subscriberPermissions |
| 安全模型 | 较松散(静态注册易被唤醒) | 严格(需要显式声明接收者权限) |
HarmonyOS 的设计更加显式化:发布者必须指定哪些权限的订阅者可以接收事件,订阅者也可以筛选发布者——双向权限校验带来更强的安全保证。
八、最佳实践与注意事项
8.1 生命周期管理
aboutToDisappear(): void {
if (this.subscriber !== null) {
commonEventManager.unsubscribe(this.subscriber);
this.subscriber = null;
}
}
页面销毁前务必取消订阅。未清理的 Subscriber 会导致内存泄漏和无效回调。
8.2 主线程回调
subscribe() 的回调在主线程执行。如果回调中包含耗时操作(如数据库写入、网络请求),应使用 @ohos.worker 将逻辑切换到子线程。
8.3 有序事件的 finishCommonEvent
在有序事件中,每个订阅者必须调用 finishCommonEvent(),否则事件链会永久阻塞——系统在等待当前订阅者完成处理。这是新手最容易踩的坑。
8.4 事件命名建议
推荐使用反向域名命名法,如 com.example.module.ACTION_NAME,避免与系统事件命名冲突。系统事件统一使用 usual.event.* 前缀。
8.5 data 大小限制
CommonEventPublishData.data 最大 64KB。超出此限制会导致发布失败。大数据传输应使用文件路径或分布式数据对象。
九、API 总结表
| API | 返回值 | 说明 |
|---|---|---|
commonEventManager.createSubscriberSync(info) |
CommonEventSubscriber |
同步创建订阅者(API 10+) |
commonEventManager.createSubscriber(info) |
Promise<CommonEventSubscriber> |
异步创建订阅者(API 9+) |
commonEventManager.subscribe(sub, cb) |
void |
订阅事件 |
commonEventManager.unsubscribe(sub) |
void |
取消订阅 |
commonEventManager.publish(event, cb) |
void |
发布简单事件 |
commonEventManager.publish(event, opts, cb) |
void |
发布带数据的事件 |
Subscriber.getCodeSync() |
number |
同步获取结果码 |
Subscriber.setCodeAndDataSync(code, data) |
void |
同步设置结果码和数据 |
Subscriber.finishCommonEvent() |
Promise<void> |
完成有序事件处理 |
Subscriber.abortCommonEventSync() |
void |
中断有序事件传递 |
十、总结
@ohos.commonEventManager 是 HarmonyOS NEXT 的进程间通信主干,提供了一套完整的发布/订阅系统:
- 零权限开销:自定义事件的发布和订阅无需任何权限声明
- 丰富的传递数据结构:
code+data+parameters三层载荷通道 - 有序事件链:基于优先级的串行传递,支持结果码修改和传递中断
- 系统事件感知:
Support枚举提供 30+ 系统公共事件,覆盖电池、电源、屏幕、时间、温控等场景 - 安全模型:双向权限校验(发布方限定接收者,订阅方筛选发布者)
这个模块与 @ohos.events.emitter(进程内事件)和 @ohos.net.http(HTTP 通信)共同构成了 HarmonyOS 应用通信的完整层级——从进程内解耦,到跨组件广播,再到网络请求,开发者可以根据通信场景精准选择对应工具。
更多推荐



所有评论(0)