HarmonyOS 事件管理进阶:on / off 精准控制回调的正确姿势
文章目录前言on vs onSubscribe两者有什么区别精准取消off(eventId, callback)once单次订阅的原始版getListenerCount订阅数统计排查泄漏神器完整生命周期管理示例一个容易踩的坑重复订阅写在最后前言近期发现一款很有意思的HarmonyOS 三方库, 地址 pura/harmony-utils(V1.4.0) , 作者是桃花镇童长老, 我这里也是直接通过该作者公布的源码进行案例编写进行,写了到目前写了一部分demo ,感觉确实很有帮助,这里呢也是开始写一个系列的演示demo 供大家参考。如有帮助可以在OpenHarmony中进行下载安装进行使用哦案例demo导航展示↓↓↓↓↓↓接下来言归正传 ↓↓↓↓上篇讲了onSubscribe和post的基本用法。这篇聊个更精细的话题怎么精准控制哪个回调被取消以及怎么监控当前的订阅数量。这在实际项目里很有用。想象一下同一个事件你注册了两个回调A 用来更新 UIB 用来记录日志。某个时刻你只想停掉 BA 继续运行——这时候unSubscribe就不够用了它会把所有回调都干掉。on vs onSubscribe两者有什么区别先搞清楚这对兄弟的区别。onSubscribe是封装版回调直接接收解包后的数据EmitterUtil.onSubscribestring(EVT_NORMAL,(msg){// msg 就是字符串直接用this.addLog([onSubscribe] →${msg});});on是原始版回调接收的是GenericEventData对象需要自己取data.dataEmitterUtil.onstring(EVT_TYPED,this.namedCallback);对应的回调定义是这样的privatenamedCallback:Callbackemitter.GenericEventDatastring(data){this.addLog([on精准] 收到:${data.data});};on的核心优势是你保存了回调函数的引用就可以在之后精准取消它。精准取消off(eventId, callback)演示页里专门有两个按钮来展示精准取消和全量取消的区别// 精准取消只取消 namedCallback 这一个回调EmitterUtil.offstring(EVT_TYPED,this.namedCallback);this.addLog([off精准] namedCallback 已取消);this.refreshCount();// 全量取消取消 EVT_TYPED 下所有回调EmitterUtil.offstring(EVT_TYPED);this.addLog([off全量] EVT_TYPED 所有订阅已取消);this.refreshCount();两者的区别off(eventId, callback)只取消指定的那个回调其他回调继续存活off(eventId)取消该事件下的所有回调精准取消的前提是你必须保存回调函数的引用。这就是为什么演示代码里要把回调定义为 class 的成员属性// 保存引用才能精准取消privatenamedCallback:Callbackemitter.GenericEventDatastring(data){this.addLog([on精准] 收到:${data.data});};如果写成匿名函数// ❌ 这样写无法精准取消每次都是新的函数对象EmitterUtil.onstring(EVT_TYPED,(data){this.addLog(data.data??);});匿名函数每次执行都会创建新的函数对象你没法拿到之前那个引用所以没办法精准取消。once单次订阅的原始版和onceSubscribe对应once是单次订阅的原始版同样接收GenericEventData// once 重新单次订阅EmitterUtil.oncestring(EVT_ONCE,(data){this.addLog([once重订] 收到:${data.data});});this.addLog([once] 已重新注册单次订阅);this.refreshCount();使用场景当你的onceSubscribe已经触发过自动取消了但你又需要再监听一次下一个事件时就可以用once手动重新注册。getListenerCount订阅数统计排查泄漏神器这个 API 很容易被忽略但在调试和排查内存问题时非常有用。refreshCount(){constnEmitterUtil.getListenerCount(EVT_NORMAL);constoEmitterUtil.getListenerCount(EVT_ONCE);consttEmitterUtil.getListenerCount(EVT_TYPED);this.listenerCountnot;this.addLog([getListenerCount] normal:${n}once:${o}typed:${t});}getListenerCount返回指定事件当前的订阅者数量。什么时候用它调试阶段确认订阅有没有正确注册数量是不是你预期的排查泄漏如果你发现某个事件的订阅数一直在增长说明某处订阅没有被取消动态控制在注册新订阅前先检查是否已经订阅过避免重复注册演示页在每次操作后都刷新订阅数显示StatelistenerCount:number0;// 顶部显示当前总订阅数Text(当前总订阅数this.listenerCount).fontSize(14).fontColor(#555)你可以通过观察这个数字的变化直观感受每次on/off操作对订阅数的影响。完整生命周期管理示例把on、off和getListenerCount结合起来一个完整的生命周期管理长这样EntryComponentstruct EmitterUtilDemoPage{Statelogs:MsgItem[][];StatelistenerCount:number0;privatenamedCallback:Callbackemitter.GenericEventDatastring(data){this.addLog([on精准] 收到:${data.data});};aboutToAppear(){// 注册时统计EmitterUtil.onstring(EVT_TYPED,this.namedCallback);this.refreshCount();}aboutToDisappear(){// 销毁时清理EmitterUtil.offstring(EVT_TYPED);}refreshCount(){constnEmitterUtil.getListenerCount(EVT_NORMAL);constoEmitterUtil.getListenerCount(EVT_ONCE);consttEmitterUtil.getListenerCount(EVT_TYPED);this.listenerCountnot;this.addLog([getListenerCount] normal:${n}once:${o}typed:${t});}}一个容易踩的坑重复订阅如果你在aboutToAppear里注册了订阅但页面因为某些原因被反复创建销毁每次创建都会注册一个新的回调。如果销毁时没有清理订阅数会一直累积。用getListenerCount可以快速验证这个问题。如果发现数量比预期大就去查对应的注册/取消逻辑。写在最后精准控制订阅的关键点就两个保存回调引用用 class 成员属性存回调不要用匿名函数配对管理注册和取消成对出现aboutToAppear和aboutToDisappear对应getListenerCount是一个非常好的调试工具建议在开发阶段多用能帮你提前发现潜在的内存泄漏问题。