HarmonyOS 鸿蒙Next 关于eventhub传入同样名称函数时,只有先传入的起效的记录

发布于 1周前 作者 htzhanglong 最后一次编辑是 5天前 来自 鸿蒙OS

本文没有结论,完全水字数的。

本来想去提个问题的,但是想了下,估计短期内无法满足,所以暂且作为一个eventhub学习记录。如果有大佬指点,先在这谢过。

学习目标:实现类的实例根据实际情况,自行订阅、取消、触发某个事件。

目标场景,有一个类A,A有一个成员a(不是static),有一个A类的对象池KK,KK中的A的实例对会按需渲染,至于哪些实例对象会渲染,会实时动态更新。某个实例的a发生变化,会通知其他实例中,与a有关的成员进行更新,起到数据同步的作用。

最初想法:在A类中,设置一个事件触发、订阅、取消的专用函数,以及一个回调执行函数。这样,当A创建实例时,实例就会有一个自己的事件回调,用来更新实例内部自有的变量。就不用每个变量单独创建一个事件触发、订阅、取消的函数,节省重复代码。A的实例在窗口显示时,需要订阅特定事件,在隐藏或者卸载UI组件时,需要取消本实例的事件订阅,其他任然显示的实例,不会取消事件。

实际场景,在开发中,因为回调函数是实例自动生成的,所以导致这个每个实例在订阅事件时,传入的函数名是一样的。经过测试,当传入已有同名的回调函数时,只有最先传入的那个会起效果。

随之也出现另一个问题,因为时更改实例内部的成员(可能是私有的),在回调函数中,用的是this来获取数据。然后导致程序运行到事件回调时崩溃,如果this在hilog中打印,这条打印语句也不会执行。

实例代码如下:

import { hilog } from "@kit.PerformanceAnalysisKit";
import { common } from "@kit.AbilityKit";
import zifu from "../../00JIYOUgongneng/ZIFUziyuan/Zifu202411100337";
import TONGYONG from "../../00JIYOUgongneng/QITAgongneng/TONGYONG";

@Entry
@Component
struct CSeventhubFankui {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('CSeventhubFankuiHelloWorld')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
    }
    .height('100%')
    .width('100%')
  }
}

@Builder
export function CSEventhubNAV(){
  CSgongneng2()
}

@Component
export struct CSgongneng2{
  private daohangqi:NavPathStack = new NavPathStack();
  private cc:common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  private changguidingyue:string = '常规订阅事件';
  private changguiCishu:number = 0;

  //类实例对象各自订阅
  @State ceshiDuixiang1:cs = new cs(this.cc,'一');
  @State ceshiDuixiang2:cs = new cs(this.cc,'二');
  //回调参数对象
  @State canshuDuixiang:cs = new cs(this.cc,'参数对象');

  @State jieguo:string = '无结果'

  @State changguiJieguo:number = 0;

  aboutToAppear(): void {

  }

  aboutToDisappear(): void {

  }

  build() {
    NavDestination(){
      Column(){
        Scroll(){
          Column({space:5}){
            Column(){
              Text('按照一般的方式去正常订阅,功能都正常,并且同一事件可挂载多个回调。')
                .width('100%')
                .textAlign(TextAlign.Start)
                .backgroundColor('#ff1c92f6')
                .height(150)

              Row(){
                Text('测试对象结果显示:')

                Text(`${this.changguiJieguo}`)
                  .layoutWeight(1)
                  .backgroundColor('#ff93b2f6')
              }
              .width('100%')
              .justifyContent(FlexAlign.Start)
              .height(50)

              Row(){
                Button('订阅')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.cc.eventHub.on(this.changguidingyue,() => {
                      this.changguiJieguo++;
                      this.changguiCishu++;
                      hilog.info(77,this.changguidingyue,'执行次数'+this.changguiCishu)
                    })
                  })

                Button('触发')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.cc.eventHub.emit(this.changguidingyue)
                  })

                Button('取消')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.cc.eventHub.off(this.changguidingyue)
                  })

                Button('重置次数')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.changguiCishu = 0;
                  })
              }
              .width('100%')
              .justifyContent(FlexAlign.Start)

            }
            .width('100%')

            Column(){
              Text('通过直接使用类已实现的事件专用函数进行订阅,使用无参回调函数,函数内部通过this获取所需数据。在回调时无法获取通过this这种方式引用的数据。')
                .width('100%')
                .textAlign(TextAlign.Start)
                .backgroundColor('#ff1c92f6')
                .height(150)

              Row(){
                Text('测试对象结果显示:')

                Text(this.jieguo)
                  .layoutWeight(1)
                  .backgroundColor('#ff93b2f6')
              }
              .width('100%')
              .justifyContent(FlexAlign.Start)
              .height(50)

              Row(){
                Button('订阅')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.ceshiDuixiang1.dingyueWucan();
                  })

                Button('触发')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.ceshiDuixiang1.dingyuechufa(this.ceshiDuixiang1);
                    this.jieguo = '' + this.ceshiDuixiang1.shuju
                  })

                Button('取消')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.ceshiDuixiang1.dingyuequxiaoWucan();
                  })
              }
              .width('100%')
              .justifyContent(FlexAlign.Start)

            }
            .width('100%')

            Column(){
              Text('通过直接使用类已实现的事件专用函数进行订阅,使用有参回调函数,函数内部通过参数对象获取所需数据。函数对象获取的数据会输出,无法获取通过this这种方式引用的数据。')
                .width('100%')
                .textAlign(TextAlign.Start)
                .backgroundColor('#ff1c92f6')

              Row(){
                Text('测试对象结果显示:')

                Text(this.jieguo)
                  .layoutWeight(1)
                  .backgroundColor('#ff93b2f6')
              }
              .width('100%')
              .justifyContent(FlexAlign.Start)
              .height(50)

              Row(){
                Button('订阅')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.ceshiDuixiang1.dingyueYoucan();
                  })

                Button('触发')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.ceshiDuixiang1.dingyuechufa(this.ceshiDuixiang1);
                    this.jieguo = '' + this.ceshiDuixiang1.shuju
                  })

                Button('取消')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.ceshiDuixiang1.dingyuequxiaoYoucan();
                  })
              }
              .width('100%')
              .height(50)

            }
            .width('100%')

            Column(){
              Text('如果同一个事件下,一个类的两个实例的函数(两个函数名一样)作为回调函数,会被当成一个,即eventhub中,只会有一个名为zhixingYoucan(),不是一个实例一个')
                .width('100%')
                .textAlign(TextAlign.Start)
                .backgroundColor('#ff1c92f6')

              Row(){
                Text('测试对象结果显示:')

                Text(this.jieguo)
                  .layoutWeight(1)
                  .backgroundColor('#ff93b2f6')
              }
              .width('100%')
              .justifyContent(FlexAlign.Start)
              .height(50)

              Row(){
                Button('第一个对象订阅')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.ceshiDuixiang1.dingyueYoucan();
                  })

                Button('第二个对象订阅')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.ceshiDuixiang2.dingyueYoucan();
                  })

                Button('触发')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.cc.eventHub.emit(this.ceshiDuixiang1.shijianming,this.ceshiDuixiang1);
                    this.jieguo = this.ceshiDuixiang1.shuju + ';' + this.ceshiDuixiang2.shuju;
                  })

                Button('取消')
                  .height(50)
                  .layoutWeight(1)
                  .onClick(() => {
                    this.cc.eventHub.off(this.ceshiDuixiang1.shijianming);
                  })
              }
              .width('100%')
              .height(50)

            }
            .width('100%')

          }
          .layoutWeight(1)
          .width('100%')
          .justifyContent(FlexAlign.Start)
        }
        .layoutWeight(1)
        .width('100%')
        .align(Alignment.Start)
      }
      .height('100%')
      .width('100%')
      .justifyContent(FlexAlign.Start)
    }
    .title('Eventhub功能测试页')
    .height('100%')
    .width('100%')
    .title(zifu.Yemianming.gongnengceshi.name)
    .onReady((e:NavDestinationContext) => {
      if (e?.pathStack) {
        this.daohangqi = e.pathStack;
      }
    })
  }
}

export class cs{
  readonly shijianming:string = '测试事件';
  readonly biaoqian:string = '测试类'
  readonly cc:common.UIAbilityContext;
  shuju:number = 0;//回调函数需要改变的数据,会在顶部text显示
  constructor(c:common.UIAbilityContext,m?:string) {
    this.cc = c;
    this.biaoqian = '实例' + TONGYONG.shijian() + m;
  }


  private zhixingWucan(){
    this.shuju++;
    hilog.info(1,this.biaoqian,this.shuju+'this数据输出'+ TONGYONG.shijian());//不会被执行
    hilog.info(1,'事件执行回调','无参回调执行,内部通过this获取数据')

  }

  private zhixingYoucan(dx:cs){
    this.shuju++;
    dx.shuju++;
    hilog.info(1,this.biaoqian,this.shuju+'this数据输出'+ TONGYONG.shijian());//不会被执行
    hilog.info(1,dx.biaoqian,dx.shuju+'参数对象,数据输出'+ TONGYONG.shijian());
    hilog.info(1,'事件执行回调','有参回调执行,内部通过this获取数据')
  }

  //类内部事件订阅专用函数,无参回调函数,通过this获取数据
  dingyueWucan(){
    this.cc.eventHub.on(this.shijianming,this.zhixingWucan)
    hilog.info(1,this.biaoqian,'订阅事件,无参回调'+ TONGYONG.shijian()+'无参回调函数')
  }

  //类内部事件订阅专用函数,有参回调函数,通过参数对象获取数据
  dingyueYoucan(){
    this.cc.eventHub.on(this.shijianming,this.zhixingYoucan)
    hilog.info(1,this.biaoqian,'订阅事件,有参回调'+ TONGYONG.shijian()+'有参回调函数')
  }

  dingyuechufa(dx:cs){
    this.cc.eventHub.emit(this.shijianming,dx)
    hilog.info(1,this.biaoqian,'触发事件'+ TONGYONG.shijian())
  }

  dingyuequxiaoWucan(){
    this.cc.eventHub.off(this.shijianming,this.zhixingWucan)
    hilog.info(1,this.biaoqian,'取消事件'+ TONGYONG.shijian())
  }

  dingyuequxiaoYoucan(){
    this.cc.eventHub.off(this.shijianming,this.zhixingYoucan)
    hilog.info(1,this.biaoqian,'取消事件'+ TONGYONG.shijian())
  }
}

// export class cs2{
//   readonly biaoqian:string = '回调参数对象'
//   jishu:number = 0;//回调函数需要改变的数据,会在顶部text显示
//   constructor() {
//   }
//
//   huidiaozhixing(){
//     this.jishu++;
//     hilog.info(2,this.biaoqian,'参数对象执行,当前计数' + this.jishu);
//   }
// }

因为考虑到性能和后期扩展,还是想使用实例的方式去生成回调函数,后来使用了其他方式,虽然满足了后面的扩展,但是增加了性能消耗、事件管理难度。

今天测试了半天,本来以为能找到更好的方法实现。结果发现,除了改底层,或者自己重新实现,其他方式都差不多。要么开发繁琐,需要在很多个地方增加代码;要么无法所有实例正常获取;要么就是无法回调。所以先随便找了种方式做着。

1 回复

针对HarmonyOS鸿蒙Next中EventHub传入同样名称函数时,只有先传入的起效的问题,这通常是由于事件注册机制导致的。在EventHub中,如果多个回调函数使用相同的名称或标识符进行注册,系统可能只会保留第一个注册的回调函数,或者后续注册的回调函数会覆盖先前的。

为了解决这个问题,你可以尝试以下方法:

  1. 确保每个事件回调函数具有唯一的标识符或名称,避免使用相同的名称进行注册。
  2. 如果确实需要使用相同的回调函数处理不同的事件,可以考虑在回调函数内部根据事件类型或参数进行区分处理。
  3. 检查事件注册的逻辑,确保没有重复注册或覆盖注册的情况。

此外,还需要注意EventHub的使用规范,如确保事件ID的一致性、检查上下文匹配等,以避免其他潜在的问题。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部