【HarmonyOS NEXT】EventHub和Emitter的使用场景与区别

一、EventHub是什么?

移动应用开发的同学应该比较了解EventHub,类似于EventBus。标准的事件广播通知,订阅,取消订阅的处理。EventHub模块提供了事件中心,提供订阅、取消订阅、触发事件的能力。

类似的框架工具有很多,例如MQTT。使用起来也超级简单,从介绍上就能大体了解使用方式,见名知意的一种快捷工具。通过一个事件ID即TAG作为唯一的key,进行事件广播通知和订阅。

在ArkUI框架中,EventHub通过单例对象的形式提供,因为放在上下文里。所以每个UIAbility对应一个EventHub。不同的UIAbility的EventHub是不同步的。

从上下文获取EventHub有两种方式:

  1. 在UIAbility中直接通过context获取:
typescript 复制代码
import { UIAbility, Context, Want, AbilityConstant } from '@kit.AbilityKit';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 获取eventHub
    let eventhub = this.context.eventHub;
    });
  }
}
  1. 在page界面或者组件中,通过UIcontext强转为UIAbilityContext获取:
typescript 复制代码
let context = getContext(this) as common.UIAbilityContext;
let eventhub = context.eventHub;

获得到EventHub单例对象后,就可以调用emit发送事件,on监听事件,off取消监听事件。进行事件广播的使用。

typescript 复制代码
  // TAG作为事件的id为字符串类型
  private EVENT_TAG: string = "TEST";
  
  /**
   * EventHub事件回调
   */
  callbackByEventHub = (content: string)=>{
    promptAction.showToast({
      message: JSON.stringify(content)
    });
  }
  
this.eventHub?.on(this.EVENT_TAG, this.callbackByEventHub); this.eventHub?.off(this.EVENT_TAG, this.callbackByEventHub);
this.eventHub?.off(this.EVENT_TAG);// 第二个参数不传,则代表EVENT_TAG下的所有注册回调都清空

详情参见官网,示例DEMO参见最后章节: UIAbility组件与UI的数据同步 EventHub API文档

二、Emitter是什么?

类似于EventHub的使用,只是内部封装了事件队列和分发的机制。多了事件id和优先级的概念。并且Emitter也可以在不同线程内调用。

Emitter区别于上文中的EventHub的事件ID,定义了一层对象进行约束。除了事件id,还需要设置事件级别。

typescript 复制代码
  private event: emitter.InnerEvent = {
    eventId: this.eventId,
    priority: emitter.EventPriority.LOW   // 定义一个eventId为1的事件,事件优先级为Low
  };

事件级别分为以下几种类型: 和EventHub不同的是,事件广播的内容,也进行了约束。 发送事件时传递的数据,支持数据类型包括Array、ArrayBuffer、Boolean、DataView、Date、Error、Map、Number、Object、Primitive(除了symbol)、RegExp、Set、String、TypedArray,数据大小最大为16M。

data是key val形式的对象,可以自己定义里面的key和val。

typescript 复制代码
    let eventData: emitter.EventData = {
      data: {
        content: '测试数据',
        id: 1,
        isEmpty: false
      }
    };

事件的广播发送,订阅和取消订阅与EventHub区别不大。只是多了once一次性监听而已。

typescript 复制代码
  private callback = (eventData: emitter.EventData): void => {

  };

    emitter.emit(this.event, eventData);
    emitter.once(this.event, this.callback)
    emitter.off(this.event.eventId, this.callback);

详情参见官网,示例DEMO参见最后章节: 使用Emitter进行线程间通信 EventHub API文档

三、EventHub和Emitter的使用场景与区别

  1. EventHub是线程内使用的时间广播工具,Emitter是线程间通信使用的工具
  2. EventHub的使用更简单,属于轻量级的广播工具,主要用于UIAbility和page之间,page和组件之间,组件和组件之间,UI和VM之间的通信,传递的数据内容形式多变且方便(...args: Object\[\])。Emitter属于重量级的广播工具,封装了优先级和队列的逻辑。传递的数据内容,必须有包裹成进行约束(emitter.EventData)
  3. Emitter监听设置,在on基础上,额外提供了once一次性监听的API。触发之后不需要再手动off取消监听。EventHub则没有。

源码DEMO解析:

typescript 复制代码
import { emitter } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct EventHubAndEmitterTestPage {

  // --------------- EventHub
  private context = getContext(this) as common.UIAbilityContext;
  private eventHub: common.EventHub | null = null;
  private EVENT_TAG: string = "TEST";

  private emitByEventHub(){
    this.eventHub = this.context.eventHub;
    this.eventHub.emit(this.EVENT_TAG, "测试数据EventHub");
  }

  /**
   * EventHub事件回调
   */
  callbackByEventHub = (content: string)=>{
    promptAction.showToast({
      message: JSON.stringify(content)
    });
  }

  private registerByEventHub = ()=>{
    this.eventHub?.on(this.EVENT_TAG, this.callbackByEventHub);
  }

  private unRegisterByEventHub = ()=>{
    this.eventHub?.off(this.EVENT_TAG, this.callbackByEventHub);
  }

  // --------------- Emitter
  private eventId: number = 1;
  private event: emitter.InnerEvent = {
    eventId: this.eventId,
    priority: emitter.EventPriority.LOW   // 定义一个eventId为1的事件,事件优先级为Low
  };

  private emitByEmitter(){
    let eventData: emitter.EventData = {
      data: {
        content: '测试数据',
        id: 1,
        isEmpty: false
      }
    };

    // 发送eventId为1的事件,事件内容为eventData
    emitter.emit(this.event, eventData);
  }

  private callback = (eventData: emitter.EventData): void => {
    promptAction.showToast({
      message: JSON.stringify(eventData)
    });
  };

  private registerByEmitter(){
    emitter.on(this.event, this.callback);
    // 监听触发后,自动消除监听。不需要手动off
    emitter.once(this.event, this.callback)
  }

  private unRegisterByEmitter(){
    emitter.off(this.event.eventId, this.callback);
  }

  // ---------------点击事件处理

  onEmitEvent = ()=>{
    this.emitByEmitter();
    this.emitByEventHub();
  }

  onRegisterEvent = ()=>{
    this.registerByEmitter();
    this.registerByEventHub();
  }

  onUnRegisterEvent = ()=>{
    this.unRegisterByEmitter();
    this.unRegisterByEventHub();

  }
  
  /**
   * 统一样式封装
   */
  @Styles ButtonStyle(){
    .width(px2vp(350))
    .height(px2vp(200))
    .margin({ top: px2vp(66) })
  }

  build() {
    Column(){
      Button("发送事件")
        .ButtonStyle()
        .onClick(this.onEmitEvent)

      Button("监听事件")
        .ButtonStyle()
        .onClick(this.onRegisterEvent)

      Button("取消事件")
        .ButtonStyle()
        .onClick(this.onUnRegisterEvent)

    }.size({
      width: "100%",
      height: "100%"
    })
  }
}
相关推荐
c2892480843s3 小时前
【鸿蒙心迹】网络请求封装实战:RCP 选型、拦截器与弱网预算
移动开发·网络请求·harmonyos·鸿蒙·rcp
卸任4 小时前
Vibe coding工具使用心得
前端·ai编程
brien95 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance5 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M5 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写5 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李5 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易5 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马6 小时前
前沿视界
前端
kyriewen6 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员