事件驱动:CommonEvent与EventHandler
从"轮询"到"通知",事件驱动让系统更高效
一、背景与动机:为什么需要事件驱动?
1.1 轮询 vs 事件驱动
假设你要等一个快递,有两种方式:
轮询方式:每隔5分钟给快递员打个电话问"到了吗?"
- 你很累,快递员也很烦
- 大部分电话都是无效的
事件驱动方式:告诉快递员"到了给我打电话"
- 你该干嘛干嘛
- 快递到了才通知你
typescript
// ❌ 轮询方式:浪费资源
while (true) {
if (isPackageArrived()) {
handlePackage()
break
}
sleep(5000) // 每5秒检查一次
}
// ✅ 事件驱动:高效响应
registerPackageListener((package) => {
handlePackage(package) // 快递到了才执行
})
1.2 鸿蒙的事件体系
鸿蒙提供了完整的事件驱动机制:
| 组件 | 作用 | 适用场景 |
|---|---|---|
| CommonEvent | 系统级事件总线 | 跨进程、系统事件 |
| EventHandler | 线程级事件队列 | 线程间通信、延时任务 |
| Emitter | 进程内事件发射器 | 组件间通信 |
graph TB
subgraph System["系统级"]
A[CommonEvent<br/>跨进程事件总线]
end
subgraph Process["进程级"]
B[Emitter<br/>进程内事件]
C[EventHandler<br/>线程事件队列]
end
subgraph Thread["线程级"]
D[主线程EventHandler]
E[工作线程EventHandler]
end
A --> B
B --> C
C --> D
C --> E
classDef primary fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
classDef info fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
classDef warning fill:#fff3e0,stroke:#e65100,stroke-width:2px
class A primary
class B,C info
class D,E warning
1.3 CommonEvent vs EventHandler
很多开发者分不清这两个:
-
CommonEvent:系统级事件总线,支持跨进程发布和订阅
- 例:电量变化、网络状态变化、屏幕亮灭
- 特点:全局可见、跨进程、需要权限
-
EventHandler:线程级事件队列,用于线程间通信
- 例:工作线程通知UI线程更新界面
- 特点:线程私有、高性能、支持延时
typescript
// CommonEvent:订阅系统电量变化
CommonEvent.subscribe({
events: ['android.intent.action.BATTERY_CHANGED']
}, (err, data) => {
console.log(`电量: ${data.data}%`)
})
// EventHandler:工作线程通知UI
class MyHandler extends EventHandler {
processEvent(eventId: number, param: unknown) {
if (eventId === 1) {
// 在主线程执行UI更新
updateUI(param)
}
}
}
二、核心原理:事件驱动机制
2.1 CommonEvent架构
graph TB
subgraph ProcessA["进程A(发布者)"]
A1[业务代码]
A2[CommonEvent.publish]
end
subgraph System["系统服务"]
B1[CommonEventManager<br/>事件管理服务]
B2[订阅者列表]
B3[粘性事件缓存]
end
subgraph ProcessB["进程B(订阅者)"]
C1[CommonEvent.subscribe]
C2[回调函数]
end
subgraph ProcessC["进程C(订阅者)"]
D1[CommonEvent.subscribe]
D2[回调函数]
end
A1 --> A2
A2 -->|Binder IPC| B1
B1 --> B2
B1 --> B3
B2 -->|匹配订阅者| C1
B2 -->|匹配订阅者| D1
C1 --> C2
D1 --> D2
classDef publisher fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
classDef manager fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef subscriber fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
class A1,A2 publisher
class B1,B2,B3 manager
class C1,C2,D1,D2 subscriber
2.2 EventHandler架构
sequenceDiagram
participant Worker as 工作线程
participant Handler as EventHandler
participant Queue as 事件队列
participant Main as 主线程
Worker->>Handler: postEvent(eventId, param)
Handler->>Queue: 入队事件
Queue->>Queue: 按时间排序
loop 事件循环
Main->>Queue: 取出事件
Queue-->>Main: 返回事件
Main->>Handler: processEvent()
Handler->>Main: 执行回调
end
2.3 事件分发流程
typescript
// CommonEvent分发流程(伪代码)
class CommonEventManager {
private subscribers: Map<string, Subscriber[]> = new Map()
private stickyEvents: Map<string, CommonEventData> = new Map()
// 发布事件
async publish(event: string, data: unknown, isSticky: boolean) {
// 如果是粘性事件,缓存起来
if (isSticky) {
this.stickyEvents.set(event, data)
}
// 查找所有订阅者
const subs = this.subscribers.get(event) || []
// 通知所有订阅者
for (const sub of subs) {
await sub.callback(data)
}
}
// 订阅事件
subscribe(event: string, callback: Function, wantSticky: boolean) {
// 添加到订阅者列表
const subs = this.subscribers.get(event) || []
subs.push({ callback })
this.subscribers.set(event, subs)
// 如果需要粘性事件,立即回调
if (wantSticky && this.stickyEvents.has(event)) {
callback(this.stickyEvents.get(event))
}
}
}
三、代码实战:CommonEvent使用
3.1 发布CommonEvent
typescript
// CommonEventPublisher.ets
import { commonEventManager, CommonEventPublishData } from '@kit.BasicServicesKit'
import hilog from '@ohos.hilog'
const TAG = 'CommonEventPublisher'
const DOMAIN = 0xFF00
/**
* CommonEvent发布者示例
*/
export class CommonEventPublisher {
/**
* 发布简单事件
*/
static async publishSimpleEvent(): Promise<void> {
try {
// 发布简单事件(无参数)
await commonEventManager.publish('com.example.MY_EVENT')
hilog.info(DOMAIN, TAG, 'Simple event published')
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
}
}
/**
* 发布带数据的事件
*/
static async publishEventWithData(): Promise<void> {
// 构建事件数据
const options: CommonEventPublishData = {
// 事件名称
event: 'com.example.DATA_EVENT',
// 事件数据(字符串)
data: 'Hello from publisher',
// 附加参数(键值对)
parameters: {
'userId': 1001,
'userName': '张三',
'timestamp': Date.now()
}
}
try {
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, 'Event with data published')
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
}
}
/**
* 发布粘性事件
* 粘性事件会被缓存,新订阅者订阅时立即收到
*/
static async publishStickyEvent(): Promise<void> {
const options: CommonEventPublishData = {
event: 'com.example.STICKY_EVENT',
data: 'This is a sticky event',
// 标记为粘性事件
isSticky: true,
parameters: {
'config': { theme: 'dark', lang: 'zh' }
}
}
try {
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, 'Sticky event published')
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
}
}
/**
* 发布有序事件
* 有序事件会按优先级依次分发给订阅者
*/
static async publishOrderedEvent(): Promise<void> {
const options: CommonEventPublishData = {
event: 'com.example.ORDERED_EVENT',
data: 'Ordered event data',
// 标记为有序事件
isOrdered: true
}
try {
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, 'Ordered event published')
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
}
}
/**
* 发布系统事件
* 需要系统权限
*/
static async publishSystemEvent(): Promise<void> {
// 注意:系统事件需要系统签名或相应权限
const options: CommonEventPublishData = {
// 系统预定义事件
event: 'android.intent.action.BATTERY_LOW',
parameters: {
'level': 15
}
}
try {
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, 'System event published')
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish system event failed: ${err.message}`)
}
}
}
3.2 订阅CommonEvent
typescript
// CommonEventSubscriber.ets
import { commonEventManager, CommonEventSubscribeInfo, CommonEventData } from '@kit.BasicServicesKit'
import hilog from '@ohos.hilog'
const TAG = 'CommonEventSubscriber'
const DOMAIN = 0xFF00
/**
* CommonEvent订阅者示例
*/
export class CommonEventSubscriber {
private subscriber: commonEventManager.CommonEventSubscriber = null
/**
* 订阅简单事件
*/
async subscribeSimpleEvent(): Promise<void> {
// 构建订阅信息
const subscribeInfo: CommonEventSubscribeInfo = {
events: ['com.example.MY_EVENT']
}
try {
// 创建订阅者
this.subscriber = await commonEventManager.createSubscriber(subscribeInfo)
// 注册回调
this.subscriber.subscribe((err, data: CommonEventData) => {
if (err) {
hilog.error(DOMAIN, TAG, `Subscribe error: ${err.message}`)
return
}
hilog.info(DOMAIN, TAG, `Received event: ${data.event}`)
hilog.info(DOMAIN, TAG, `Event data: ${data.data}`)
})
hilog.info(DOMAIN, TAG, 'Subscribed to simple event')
} catch (err) {
hilog.error(DOMAIN, TAG, `Subscribe failed: ${err.message}`)
}
}
/**
* 订阅多个事件
*/
async subscribeMultipleEvents(): Promise<void> {
const subscribeInfo: CommonEventSubscribeInfo = {
// 订阅多个事件
events: [
'com.example.EVENT_A',
'com.example.EVENT_B',
'com.example.EVENT_C'
]
}
try {
this.subscriber = await commonEventManager.createSubscriber(subscribeInfo)
this.subscriber.subscribe((err, data: CommonEventData) => {
if (err) {
hilog.error(DOMAIN, TAG, `Error: ${err.message}`)
return
}
// 根据事件类型处理
switch (data.event) {
case 'com.example.EVENT_A':
this.handleEventA(data)
break
case 'com.example.EVENT_B':
this.handleEventB(data)
break
case 'com.example.EVENT_C':
this.handleEventC(data)
break
}
})
hilog.info(DOMAIN, TAG, 'Subscribed to multiple events')
} catch (err) {
hilog.error(DOMAIN, TAG, `Subscribe failed: ${err.message}`)
}
}
private handleEventA(data: CommonEventData) {
hilog.info(DOMAIN, TAG, `Event A: ${data.data}`)
}
private handleEventB(data: CommonEventData) {
hilog.info(DOMAIN, TAG, `Event B: ${data.data}`)
}
private handleEventC(data: CommonEventData) {
hilog.info(DOMAIN, TAG, `Event C: ${data.data}`)
}
/**
* 订阅粘性事件
* 订阅时会立即收到之前发布的粘性事件
*/
async subscribeStickyEvent(): Promise<void> {
const subscribeInfo: CommonEventSubscribeInfo = {
events: ['com.example.STICKY_EVENT'],
// 请求接收粘性事件
getStickEvent: true
}
try {
this.subscriber = await commonEventManager.createSubscriber(subscribeInfo)
this.subscriber.subscribe((err, data: CommonEventData) => {
if (err) {
hilog.error(DOMAIN, TAG, `Error: ${err.message}`)
return
}
hilog.info(DOMAIN, TAG, `Received sticky event: ${data.data}`)
// 读取附加参数
const params = data.parameters
if (params && params.config) {
hilog.info(DOMAIN, TAG, `Config: ${JSON.stringify(params.config)}`)
}
})
hilog.info(DOMAIN, TAG, 'Subscribed to sticky event')
} catch (err) {
hilog.error(DOMAIN, TAG, `Subscribe failed: ${err.message}`)
}
}
/**
* 取消订阅
*/
async unsubscribe(): Promise<void> {
if (this.subscriber) {
try {
await this.subscriber.unsubscribe()
hilog.info(DOMAIN, TAG, 'Unsubscribed')
this.subscriber = null
} catch (err) {
hilog.error(DOMAIN, TAG, `Unsubscribe failed: ${err.message}`)
}
}
}
}
3.3 EventHandler使用
typescript
// EventHandlerDemo.ets
import { EventHandler, EventRunner } from '@kit.BasicServicesKit'
import hilog from '@ohos.hilog'
const TAG = 'EventHandlerDemo'
const DOMAIN = 0xFF00
/**
* 自定义EventHandler
*/
export class MyEventHandler extends EventHandler {
/**
* 处理事件的核心方法
* 在关联的线程中执行
*/
processEvent(eventId: number, param: unknown): void {
hilog.info(DOMAIN, TAG, `Processing event: ${eventId}`)
switch (eventId) {
case 1:
this.handleUpdateUI(param)
break
case 2:
this.handleDataLoaded(param)
break
case 3:
this.handleNetworkResponse(param)
break
default:
hilog.warn(DOMAIN, TAG, `Unknown event: ${eventId}`)
}
}
private handleUpdateUI(param: unknown) {
// 在主线程更新UI
hilog.info(DOMAIN, TAG, `Update UI: ${JSON.stringify(param)}`)
}
private handleDataLoaded(param: unknown) {
hilog.info(DOMAIN, TAG, `Data loaded: ${JSON.stringify(param)}`)
}
private handleNetworkResponse(param: unknown) {
hilog.info(DOMAIN, TAG, `Network response: ${JSON.stringify(param)}`)
}
}
/**
* EventHandler使用示例
*/
export class EventHandlerExample {
private mainHandler: MyEventHandler = null
private workerHandler: MyEventHandler = null
private workerRunner: EventRunner = null
/**
* 初始化主线程Handler
*/
initMainHandler(): void {
// 获取主线程的EventRunner
const mainRunner = EventRunner.getMainEventRunner()
// 创建主线程Handler
this.mainHandler = new MyEventHandler(mainRunner)
hilog.info(DOMAIN, TAG, 'Main handler initialized')
}
/**
* 初始化工作线程Handler
*/
async initWorkerHandler(): Promise<void> {
// 创建工作线程的EventRunner
this.workerRunner = EventRunner.create('WorkerThread')
// 启动事件循环
this.workerRunner.run()
// 创建工作线程Handler
this.workerHandler = new MyEventHandler(this.workerRunner)
hilog.info(DOMAIN, TAG, 'Worker handler initialized')
}
/**
* 发送即时事件
*/
sendImmediateEvent(): void {
// 发送到主线程
this.mainHandler.sendEvent({
eventId: 1,
param: { message: 'Update UI now' }
})
hilog.info(DOMAIN, TAG, 'Immediate event sent')
}
/**
* 发送延时事件
*/
sendDelayedEvent(): void {
// 延时5秒发送
this.mainHandler.sendEvent({
eventId: 2,
param: { message: 'Delayed data' }
}, 5000) // 延时5000毫秒
hilog.info(DOMAIN, TAG, 'Delayed event scheduled (5s)')
}
/**
* 发送周期性事件
*/
sendPeriodicEvent(): void {
// 每3秒发送一次
this.mainHandler.sendEvent({
eventId: 3,
param: { message: 'Periodic update' }
}, 3000, true) // 第三个参数表示周期性
hilog.info(DOMAIN, TAG, 'Periodic event scheduled (every 3s)')
}
/**
* 线程间通信示例
* 工作线程完成任务后通知主线程
*/
async crossThreadCommunication(): Promise<void> {
// 在工作线程执行任务
this.workerHandler.sendEvent({
eventId: 100,
param: { task: 'heavyWork' }
})
// 模拟工作线程处理后通知主线程
setTimeout(() => {
// 工作线程发送结果到主线程
this.mainHandler.sendEvent({
eventId: 1,
param: {
result: 'Work completed',
data: [1, 2, 3, 4, 5]
}
})
}, 2000)
hilog.info(DOMAIN, TAG, 'Cross-thread communication started')
}
/**
* 移除事件
*/
removeEvent(eventId: number): void {
this.mainHandler.removeEvent(eventId)
hilog.info(DOMAIN, TAG, `Event ${eventId} removed`)
}
/**
* 清理资源
*/
cleanup(): void {
if (this.workerRunner) {
this.workerRunner.stop()
}
hilog.info(DOMAIN, TAG, 'Cleanup completed')
}
}
3.4 实战:网络请求与UI更新
typescript
// NetworkWithEventHandler.ets
import { EventHandler, EventRunner } from '@kit.BasicServicesKit'
import http from '@ohos.net.http'
import hilog from '@ohos.hilog'
const TAG = 'NetworkDemo'
const DOMAIN = 0xFF00
// 事件ID定义
const enum EventId {
NETWORK_SUCCESS = 1,
NETWORK_ERROR = 2,
UPDATE_PROGRESS = 3
}
/**
* 网络请求结果
*/
interface NetworkResult {
url: string
data: string
statusCode: number
}
/**
* 网络请求管理器
* 使用EventHandler实现线程安全的网络请求
*/
export class NetworkManager {
private mainHandler: EventHandler
private httpRequest: http.HttpRequest
constructor() {
// 获取主线程Handler
const mainRunner = EventRunner.getMainEventRunner()
this.mainHandler = new EventHandler(mainRunner)
// 创建HTTP请求对象
this.httpRequest = http.createHttp()
}
/**
* 发起网络请求
* 在工作线程执行,完成后通知主线程
*/
async fetchData(url: string): Promise<void> {
hilog.info(DOMAIN, TAG, `Fetching: ${url}`)
try {
// 发起请求
const response = await this.httpRequest.request(url, {
method: http.RequestMethod.GET,
connectTimeout: 60000,
readTimeout: 60000
})
// 请求成功,通知主线程
const result: NetworkResult = {
url: url,
data: response.result as string,
statusCode: response.responseCode
}
this.mainHandler.sendEvent({
eventId: EventId.NETWORK_SUCCESS,
param: result
})
} catch (err) {
// 请求失败,通知主线程
this.mainHandler.sendEvent({
eventId: EventId.NETWORK_ERROR,
param: {
url: url,
error: err.message
}
})
}
}
/**
* 带进度的下载
*/
async downloadWithProgress(url: string, destPath: string): Promise<void> {
hilog.info(DOMAIN, TAG, `Downloading: ${url}`)
// 模拟进度更新
for (let progress = 0; progress <= 100; progress += 10) {
// 通知主线程更新进度
this.mainHandler.sendEvent({
eventId: EventId.UPDATE_PROGRESS,
param: {
url: url,
progress: progress
}
})
// 模拟下载延迟
await new Promise(resolve => setTimeout(resolve, 500))
}
// 下载完成
this.mainHandler.sendEvent({
eventId: EventId.NETWORK_SUCCESS,
param: {
url: url,
data: destPath,
statusCode: 200
}
})
}
/**
* 销毁
*/
destroy(): void {
this.httpRequest.destroy()
}
}
/**
* UI组件使用示例
*/
@Entry
@Component
struct NetworkPage {
@State data: string = 'Loading...'
@State progress: number = 0
@State errorMessage: string = ''
private networkManager: NetworkManager = new NetworkManager()
private eventHandler: EventHandler
aboutToAppear() {
// 创建EventHandler处理网络结果
const mainRunner = EventRunner.getMainEventRunner()
this.eventHandler = new EventHandler(mainRunner)
// 处理事件
this.eventHandler.setEventHandler((eventId, param) => {
switch (eventId) {
case EventId.NETWORK_SUCCESS:
this.handleSuccess(param as NetworkResult)
break
case EventId.NETWORK_ERROR:
this.handleError(param)
break
case EventId.UPDATE_PROGRESS:
this.handleProgress(param)
break
}
})
// 发起请求
this.networkManager.fetchData('https://api.example.com/data')
}
aboutToDisappear() {
this.networkManager.destroy()
}
private handleSuccess(result: NetworkResult) {
this.data = result.data
hilog.info(DOMAIN, TAG, `Data loaded: ${result.data.length} chars`)
}
private handleError(error: { url: string, error: string }) {
this.errorMessage = error.error
hilog.error(DOMAIN, TAG, `Error: ${error.error}`)
}
private handleProgress(info: { url: string, progress: number }) {
this.progress = info.progress
}
build() {
Column() {
Text('Network Data')
.fontSize(24)
.fontWeight(FontWeight.Bold)
if (this.errorMessage) {
Text(`Error: ${this.errorMessage}`)
.fontColor(Color.Red)
} else if (this.progress > 0 && this.progress < 100) {
Progress({ value: this.progress, total: 100, type: ProgressType.Linear })
.width('80%')
Text(`${this.progress}%`)
} else {
Text(this.data)
.maxLines(10)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
四、踩坑与注意事项
4.1 坑一:CommonEvent订阅未取消
问题:订阅后忘记取消,导致内存泄漏和重复回调。
typescript
// ❌ 错误示范:只订阅不取消
async subscribeEvent() {
const subscriber = await commonEventManager.createSubscriber({
events: ['com.example.EVENT']
})
subscriber.subscribe((err, data) => {
// 处理事件
})
// 没有保存subscriber引用,无法取消!
}
// ✅ 正确做法:保存引用,适时取消
export class MyComponent {
private subscriber: commonEventManager.CommonEventSubscriber = null
async aboutToAppear() {
this.subscriber = await commonEventManager.createSubscriber({
events: ['com.example.EVENT']
})
this.subscriber.subscribe((err, data) => {
// 处理事件
})
}
async aboutToDisappear() {
// 组件销毁时取消订阅
if (this.subscriber) {
await this.subscriber.unsubscribe()
}
}
}
4.2 坑二:EventHandler在错误线程
问题:在非主线程创建的EventHandler无法更新UI。
typescript
// ❌ 错误示范:在工作线程创建Handler
import taskpool from '@ohos.taskpool'
@taskpool.task
function backgroundWork() {
// 这里创建的Handler不在主线程!
const handler = new EventHandler(EventRunner.create())
handler.sendEvent({
eventId: 1,
param: { data: 'result' }
})
// 这个事件不会在主线程执行
}
// ✅ 正确做法:使用主线程Handler
export class CorrectExample {
private mainHandler: EventHandler
constructor() {
// 在主线程创建Handler
const mainRunner = EventRunner.getMainEventRunner()
this.mainHandler = new EventHandler(mainRunner)
}
async doWork() {
// 工作线程执行任务
const result = await taskpool.execute(backgroundTask)
// 通知主线程
this.mainHandler.sendEvent({
eventId: 1,
param: result
})
}
}
4.3 坑三:CommonEvent权限问题
问题:某些系统事件需要特殊权限。
typescript
// ❌ 订阅系统事件可能失败
await commonEventManager.createSubscriber({
events: ['android.intent.action.BOOT_COMPLETED'] // 需要系统权限
})
// ✅ 检查权限并处理错误
async subscribeSystemEvent() {
try {
const subscriber = await commonEventManager.createSubscriber({
events: ['android.intent.action.BOOT_COMPLETED']
})
subscriber.subscribe((err, data) => {
if (err) {
if (err.code === 201) {
// 权限不足
hilog.error(DOMAIN, TAG, 'Permission denied')
} else {
hilog.error(DOMAIN, TAG, `Error: ${err.message}`)
}
return
}
// 处理事件
})
} catch (err) {
hilog.error(DOMAIN, TAG, `Subscribe failed: ${err.message}`)
}
}
4.4 坑四:事件循环阻塞
问题:EventHandler的事件处理函数执行时间过长,会阻塞后续事件。
typescript
// ❌ 错误示范:事件处理函数执行太久
class SlowHandler extends EventHandler {
processEvent(eventId: number, param: unknown) {
// 耗时操作阻塞事件循环
for (let i = 0; i < 1000000000; i++) {
// 模拟耗时计算
}
// 后续事件都要等待
}
}
// ✅ 正确做法:耗时操作异步处理
class FastHandler extends EventHandler {
processEvent(eventId: number, param: unknown) {
if (eventId === 1) {
// 快速提取参数
const data = param as HeavyData
// 提交到工作线程处理
taskpool.execute(() => {
processHeavyData(data)
})
// 快速返回,不阻塞事件循环
}
}
}
4.5 坑五:粘性事件堆积
问题:粘性事件会一直缓存,可能导致数据过期。
typescript
// ❌ 粘性事件一直存在
await commonEventManager.publish({
event: 'com.example.CONFIG',
data: JSON.stringify({ theme: 'light' }),
isSticky: true
})
// 后续修改配置,但旧事件还在
await commonEventManager.publish({
event: 'com.example.CONFIG',
data: JSON.stringify({ theme: 'dark' }),
isSticky: true
})
// 现在有两个粘性事件!
// ✅ 方案:发布前先清除旧的粘性事件
async publishConfig(config: object) {
// 清除旧的粘性事件(如果API支持)
// 或者在订阅时只处理最新的
await commonEventManager.publish({
event: 'com.example.CONFIG',
data: JSON.stringify(config),
isSticky: true
})
}
五、HarmonyOS 6适配指南
5.1 API变更
| 变更项 | HarmonyOS 5 | HarmonyOS 6 |
|---|---|---|
| CommonEvent导入 | @ohos.commonEvent |
@kit.BasicServicesKit |
| 订阅方式 | 回调嵌套 | Promise + 回调分离 |
| EventHandler创建 | new EventHandler() |
new EventHandler(runner) |
| 事件参数 | 多参数 | 单对象参数 |
5.2 新增功能
typescript
// HarmonyOS 6新增:事件优先级
const subscribeInfo: CommonEventSubscribeInfo = {
events: ['com.example.EVENT'],
// 新增:设置订阅优先级(有序事件)
priority: 100 // 数值越大优先级越高
}
// HarmonyOS 6新增:事件过滤
const subscribeInfo: CommonEventSubscribeInfo = {
events: ['com.example.EVENT'],
// 新增:设置发布者UID过滤
publisherDeviceId: 'local', // 只接收本地设备事件
// 新增:设置发布者PID过滤
publisherPid: 12345 // 只接收特定进程的事件
}
// HarmonyOS 6新增:异步订阅
async function asyncSubscribe() {
const subscriber = await commonEventManager.createSubscriber(subscribeInfo)
// 使用Promise风格的订阅
subscriber.on('receive', (data: CommonEventData) => {
console.log(`Received: ${data.event}`)
})
// 错误处理
subscriber.on('error', (err: Error) => {
console.error(`Error: ${err.message}`)
})
}
5.3 性能优化
typescript
// HarmonyOS 6:批量发送事件
class BatchEventHandler extends EventHandler {
// 批量发送,减少上下文切换
sendBatchEvents(events: Array<{ id: number, param: unknown }>) {
for (const event of events) {
this.sendEvent({
eventId: event.id,
param: event.param
})
}
}
// 使用延时合并相同事件
private pendingEvents: Map<number, unknown> = new Map()
sendDebounced(eventId: number, param: unknown, delay: number = 100) {
// 保存最新参数
this.pendingEvents.set(eventId, param)
// 延时发送,多次调用会覆盖
this.sendEvent({ eventId: -1, param: { type: 'debounce', targetId: eventId } }, delay)
}
processEvent(eventId: number, param: unknown) {
if (eventId === -1) {
// 处理延时合并
const { type, targetId } = param as { type: string, targetId: number }
if (type === 'debounce') {
const actualParam = this.pendingEvents.get(targetId)
this.pendingEvents.delete(targetId)
// 发送实际事件
this.processEvent(targetId, actualParam)
}
return
}
// 正常事件处理
// ...
}
}
六、总结一下下
6.1 核心要点回顾
graph TB
A[事件驱动核心] --> B[CommonEvent]
A --> C[EventHandler]
B --> B1[跨进程通信]
B --> B2[系统事件]
B --> B3[粘性事件]
C --> C1[线程间通信]
C --> C2[延时任务]
C --> C3[周期任务]
classDef primary fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
classDef info fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
classDef warning fill:#fff3e0,stroke:#e65100,stroke-width:2px
class A primary
class B,C info
class B1,B2,B3,C1,C2,C3 warning
6.2 最佳实践清单
- CommonEvent订阅后记得取消
- 使用主线程EventHandler更新UI
- 处理权限不足的情况
- 事件处理函数快速返回
- 耗时操作异步处理
- 粘性事件注意数据时效性
- 使用try-catch处理错误
- 合理设置事件优先级
6.3 选择指南
| 场景 | 推荐方案 |
|---|---|
| 跨进程通信 | CommonEvent |
| 系统事件监听 | CommonEvent |
| 线程间通信 | EventHandler |
| UI更新通知 | EventHandler(主线程) |
| 延时任务 | EventHandler |
| 组件间通信 | Emitter |