鸿蒙 EventBus 与 Message 通信机制详解

发布时间:2026/6/26 6:32:54

鸿蒙 EventBus 与 Message 通信机制详解 鸿蒙 EventBus 与 Message 通信机制详解适用版本HarmonyOS NEXT / API 12语言ArkTS一、为什么需要事件通信在鸿蒙应用开发中组件之间、页面之间、线程之间经常需要传递数据或通知状态变化。直接持有引用会造成强耦合因此需要专门的通信机制。鸿蒙提供了两套主流方案机制核心 API适用场景EventBus事件总线ohos.events.emitter/context.eventHub同进程内组件、页面之间解耦通信Message消息传递WorkerpostMessage/ TaskPool主线程与子线程之间的跨线程通信二、EventBus进程内事件总线2.1 Emitter —— 全局事件总线emitter是鸿蒙提供的内置事件总线基于发布-订阅模式订阅者和发布者完全解耦。引入方式import{emitter}fromkit.BasicServicesKit;订阅事件// 定义事件 IDnumber 类型constLOGIN_SUCCESS_EVENT:emitter.InnerEvent{eventId:1001,};// 订阅持久监听emitter.on(LOGIN_SUCCESS_EVENT,(data:emitter.EventData){constuserId:stringdata.data?.[userId]asstring??;console.info(收到登录成功事件userId:${userId});});// 订阅只接收一次自动取消emitter.once(LOGIN_SUCCESS_EVENT,(data:emitter.EventData){console.info(只接收一次的登录事件);});发布事件// 发布事件并携带数据emitter.emit(LOGIN_SUCCESS_EVENT,{data:{userId:user_123,nickname:张三,}});取消订阅// 取消对某个事件的所有订阅emitter.off(1001);带优先级发布constevent:emitter.InnerEvent{eventId:1002,priority:emitter.EventPriority.HIGH,// HIGH / LOW / IMMEDIATE / IDLE};emitter.emit(event,{data:{msg:高优先级消息}});2.2 EventHub —— UIAbility 内部事件总线EventHub绑定在UIAbilityContext上事件生命周期随 UIAbility 结束而结束更适合 UIAbility 内部的页面间通信。// 在 UIAbility 中发布import{UIAbility,Want,AbilityConstant}fromkit.AbilityKit;exportdefaultclassEntryAbilityextendsUIAbility{onForeground(){// 发布事件this.context.eventHub.emit(userLogout,{reason:主动退出});}}// 在页面中订阅import{common}fromkit.AbilityKit;EntryComponentV2struct ProfilePage{privatecontextgetContext(this)ascommon.UIAbilityContext;aboutToAppear():void{this.context.eventHub.on(userLogout,(data:Recordstring,string){console.info(用户退出原因${data.reason});});}aboutToDisappear():void{// 页面销毁时一定要取消订阅this.context.eventHub.off(userLogout);}}2.3 自定义 EventBusTypeScript 实现项目中也可以封装一个轻量 EventBus 单例typeEventCallback(data:object)void;classEventBus{privatestaticinstance:EventBus;privatelisteners:Mapstring,EventCallback[]newMap();staticgetInstance():EventBus{if(!EventBus.instance){EventBus.instancenewEventBus();}returnEventBus.instance;}on(event:string,callback:EventCallback):void{if(!this.listeners.has(event)){this.listeners.set(event,[]);}this.listeners.get(event)!.push(callback);}off(event:string,callback?:EventCallback):void{if(!callback){this.listeners.delete(event);return;}constcbsthis.listeners.get(event)??[];this.listeners.set(event,cbs.filter(cbcb!callback));}emit(event:string,data:object{}):void{constcbsthis.listeners.get(event)??[];cbs.forEach(cbcb(data));}}exportconsteventBusEventBus.getInstance();使用// 订阅eventBus.on(refresh,(data){console.info(刷新列表,JSON.stringify(data));});// 发布eventBus.emit(refresh,{tabId:recommend});// 取消eventBus.off(refresh);三、Message跨线程消息传递Message机制用于主线程与子线程之间通信核心是Worker和TaskPool的postMessage/sendData。重要ArkTS 中不同线程有各自独立的内存空间不能共享对象引用只能通过消息序列化传递数据。3.1 Worker postMessageWorker 适合需要长期驻留、双向通信的子线程场景如持续的 WebSocket 连接、音视频处理等。目录结构entry/src/main/ets/ ├── pages/ │ └── Index.ets ← 主线程页面 └── workers/ └── MyWorker.ets ← Worker 子线程Worker 子线程MyWorker.etsimport{worker,MessageEvents,ErrorEvent}fromkit.ArkTS;// 获取当前 Worker 的通信端口constworkerPort:worker.ThreadWorkerGlobalScopeworker.workerPort;// 监听主线程消息workerPort.onmessage(event:MessageEvents){constdataevent.dataasRecordstring,string;console.info([Worker] 收到主线程消息:${JSON.stringify(data)});// 处理耗时任务...constresultheavyTask(data.input);// 回传结果给主线程workerPort.postMessage({result,status:success});};workerPort.onerror(err:ErrorEvent){console.error([Worker] 错误:${err.message});};functionheavyTask(input:string):string{// 模拟耗时计算returninput.toUpperCase();}主线程Index.etsimport{worker,MessageEvents}fromkit.ArkTS;EntryComponentV2struct Index{privatemyWorker:worker.ThreadWorker|nullnull;Localresult:string;aboutToAppear():void{// 创建 Workerthis.myWorkernewworker.ThreadWorker(entry/ets/workers/MyWorker.ets);// 监听 Worker 回传的消息this.myWorker.onmessage(event:MessageEvents){constdataevent.dataasRecordstring,string;this.resultdata.result;console.info([主线程] Worker 回传:${this.result});};}aboutToDisappear():void{// 页面销毁时终止 Worker释放资源this.myWorker?.terminate();}build(){Column({space:16}){Text(处理结果:${this.result})Button(发送任务给 Worker).onClick((){// 向 Worker 发送消息this.myWorker?.postMessage({input:hello harmony});})}.width(100%).height(100%).justifyContent(FlexAlign.Center)}}3.2 TaskPool sendData推荐TaskPool 是更现代的方案适合一次性并发任务由系统自动管理线程池开发者无需手动创建/销毁线程。import{taskpool}fromkit.ArkTS;// 定义任务函数必须是顶层函数或静态方法不能是箭头函数ConcurrentfunctionprocessData(input:string):string{// 在子线程中执行returninput.split().reverse().join();}// 主线程调用asyncfunctionrunTask():Promisevoid{consttasknewtaskpool.Task(processData,hello);// 等待任务完成并获取结果constresult:stringawaittaskpool.execute(task)asstring;console.info(任务结果:${result});// 输出: olleh}TaskPool 发送中间消息sendData当任务需要在执行过程中不断向主线程汇报进度时// 子线程任务ConcurrentfunctiondownloadFile(url:string):void{for(leti0;i100;i10){// 向主线程发送进度taskpool.Task.sendData(i);// 模拟耗时}}// 主线程consttasknewtaskpool.Task(downloadFile,https://example.com/file.zip);// 注册进度回调task.onReceiveData((progress:number){console.info(下载进度:${progress}%);});taskpool.execute(task);四、EventBus vs Message 核心区别对比维度EventBusemitter / EventHubMessageWorker / TaskPool通信范围同一线程主线程内的组件/页面间主线程 ↔ 子线程跨线程数据共享可直接传递对象引用数据需要可序列化不能传引用生命周期需手动取消订阅否则内存泄漏Worker 需手动 terminateTaskPool 自动管理使用场景页面刷新、登录状态同步、Tab切换通知图片处理、加密运算、大数据解析等耗时任务是否阻塞 UI不阻塞同线程但异步回调不阻塞任务在子线程执行线程安全单线程无需考虑需注意数据竞争ArkTS 通过隔离内存保证安全复杂度低几行代码即可使用较高需要额外的 Worker 文件五、选型建议需要通信 │ ├── 是否涉及耗时计算16ms 会卡 UI │ ├── 是 → 使用 TaskPool一次性任务或 Worker持续任务 │ └── 否 → 继续往下 │ ├── 是否跨 UIAbility │ ├── 是 → 使用 emitter全局 │ └── 否 → 同一 UIAbility 内 │ └── 是否需要随 UIAbility 自动清理 ├── 是 → 使用 EventHubcontext.eventHub └── 否 → 使用 emitter 或自定义 EventBus典型场景对照场景推荐方案用户登录后刷新首页emitter.emit购物车数量更新通知各页面emitter或自定义 EventBus退出登录清理页面状态context.eventHub.emit大图压缩/格式转换TaskPool实时音视频数据处理WorkerWebSocket 长连接管理Worker常驻子线程批量数据加密上传TaskPool并发多任务六、常见坑点EventBus 篇坑 1忘记取消订阅导致内存泄漏// ❌ 错误aboutToDisappear 中没有取消订阅aboutToAppear():void{emitter.on({eventId:1001},(){...});}// ✅ 正确配对 offaboutToAppear():void{emitter.on({eventId:1001},this.onEvent);}aboutToDisappear():void{emitter.off(1001);}坑 2eventId 冲突全局使用emitter时不同模块可能使用相同的 eventId建议统一在常量文件中定义// events/EventIds.tsexportconstEventIds{LOGIN_SUCCESS:1001,CART_UPDATE:1002,REFRESH_HOME:1003,};Message 篇坑 3Worker 中不能使用 UI 相关 APIWorker 线程没有 UI 上下文不能调用router、promptAction、AppStorage等 API。坑 4传递不可序列化的对象// ❌ 错误传递了含函数的对象worker.postMessage({callback:(){}});// 报错// ✅ 正确只传可序列化的纯数据worker.postMessage({userId:123,action:refresh});坑 5Concurrent 函数不能捕获外部变量constprefixhello;// 外部变量// ❌ 错误Concurrent 函数不能闭包捕获外部变量ConcurrentfunctionbadTask():string{returnprefix world;// 编译报错}// ✅ 正确通过参数传入ConcurrentfunctiongoodTask(prefix:string):string{returnprefix world;}taskpool.execute(newtaskpool.Task(goodTask,prefix));七、总结EventBusemitter / EventHub本质是主线程内的发布-订阅模式解决的是组件解耦问题代码简单但要注意及时取消订阅。MessageWorker / TaskPool本质是跨线程通信解决的是耗时任务不阻塞 UI的问题数据需要可序列化。两者并不互斥实际项目中常常配合使用Worker 在子线程完成计算后通过postMessage回传结果主线程再通过emitter通知相关组件更新 UI。

相关新闻