引言

在移动应用架构中,组件间的通信始终是一个核心命题。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 — 有序事件的发布与结果码处理
  • 数据流CommonEventPublishDataCommonEventData — 发布端到接收端的数据传递
  • 系统事件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 };    // 附加键值对
}

codedata 是发布者向订阅者传递信息的两个主通道。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 的"镜像"——发布时的 codedataparameters 原封不动传递到接收端。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);

CommonEventSubscribeInfoevents 数组支持多个事件名——一个 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 个交互点

  1. 订阅自定义事件 — 输入事件名,点击"订阅"创建 Subscriber 并注册回调,状态实时反馈
  2. 发布事件 — 编辑事件名、code、data,勾选有序/无序,点击"发布"触发;日志立即显示发布成功和接收确认
  3. 有序事件切换 — 勾选有序事件 Toggle 后发布,Demo 演示发布端如何通过 isOrdered 参数控制事件传递模式
  4. 实时事件日志 — 所有操作(订阅/取消/发布/接收/异常)均以带时间戳的彩色日志呈现,最近 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 的进程间通信主干,提供了一套完整的发布/订阅系统:

  1. 零权限开销:自定义事件的发布和订阅无需任何权限声明
  2. 丰富的传递数据结构code + data + parameters 三层载荷通道
  3. 有序事件链:基于优先级的串行传递,支持结果码修改和传递中断
  4. 系统事件感知Support 枚举提供 30+ 系统公共事件,覆盖电池、电源、屏幕、时间、温控等场景
  5. 安全模型:双向权限校验(发布方限定接收者,订阅方筛选发布者)

这个模块与 @ohos.events.emitter(进程内事件)和 @ohos.net.http(HTTP 通信)共同构成了 HarmonyOS 应用通信的完整层级——从进程内解耦,到跨组件广播,再到网络请求,开发者可以根据通信场景精准选择对应工具。


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐