最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

HarmonyOS?6.0應(yīng)用級事件總線與組件間通信實(shí)例詳解

 更新時間:2026年07月28日 09:54:10   作者:youtootech  
在鴻蒙Next開發(fā)過程中,組件間的數(shù)據(jù)傳遞是開發(fā)者需要掌握的重要技能,這篇文章主要介紹了HarmonyOS?6.0應(yīng)用級事件總線與組件間通信的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

組件通信有@Prop/@Link、@Provide/@Consume、@Watch這些"正規(guī)軍"。但有些場景它們搞不定——跨Ability通信、非父子組件通信、一對多廣播、解耦通信。這時候就得用事件總線。HarmonyOS提供了Emitter和CommonEvent兩套方案,分別管應(yīng)用內(nèi)和應(yīng)用間,但很多人分不清什么時候用哪個。

通信方案對比

方案通信范圍耦合度適用場景
@Prop/@Link父子組件父子數(shù)據(jù)同步
@Provide/@Consume跨層級祖孫組件通信
AppStorage全局全局狀態(tài)共享
Emitter應(yīng)用內(nèi)任意組件間解耦通信
CommonEvent跨應(yīng)用系統(tǒng)事件、跨應(yīng)用通知

Emitter是應(yīng)用內(nèi)的事件總線,CommonEvent是系統(tǒng)級的事件廣播。 大多數(shù)場景用Emitter就夠了,CommonEvent只在需要跟系統(tǒng)或其他應(yīng)用通信時才用。

Emitter基礎(chǔ)

Emitter是@kit.BasicServicesKit提供的進(jìn)程內(nèi)事件機(jī)制:

import { emitter } from '@kit.BasicServicesKit';

// 定義事件ID
const EVENT_LOGIN_SUCCESS = 1001;
const EVENT_CART_UPDATED = 1002;

// 發(fā)送事件
emitter.emit({
  eventId: EVENT_LOGIN_SUCCESS,
  parameters: { userId: '12345', userName: '張三' }
});

// 接收事件
emitter.on({
  eventId: EVENT_LOGIN_SUCCESS
}, (eventData: emitter.EventData) => {
  let params = eventData.parameters;
  let userId = params['userId'] as string;
  this.onLoginSuccess(userId);
});

eventId是數(shù)字類型的事件標(biāo)識,自己定義。parameters攜帶數(shù)據(jù),類型是Record<string, Object>。

關(guān)鍵:Emitter是進(jìn)程內(nèi)的,同一個應(yīng)用內(nèi)才能收發(fā)。 跨進(jìn)程(不同Ability運(yùn)行在不同進(jìn)程)時Emitter不生效。

Emitter取消訂閱

// 訂閱時保存回調(diào)引用
private loginCallback = (eventData: emitter.EventData) => {
  // 處理
}

aboutToAppear(): void {
  emitter.on({ eventId: EVENT_LOGIN_SUCCESS }, this.loginCallback);
}

aboutToDisappear(): void {
  emitter.off(EVENT_LOGIN_SUCCESS, this.loginCallback);
}

必須在aboutToDisappear中off取消訂閱,否則組件銷毀后回調(diào)還在執(zhí)行,導(dǎo)致內(nèi)存泄漏和空引用crash。

off必須傳跟on相同的回調(diào)函數(shù)引用。所以回調(diào)不能寫成匿名函數(shù),要保存為類成員變量。

一對多廣播

Emitter天然支持一對多——多個組件訂閱同一個eventId,emit時所有訂閱者都會收到:

// 購物車頁面
emitter.emit({ eventId: EVENT_CART_UPDATED, parameters: { count: 5 } });

// TabBar組件
emitter.on({ eventId: EVENT_CART_UPDATED }, (data) => {
  this.cartBadge = data.parameters['count'] as number;
});

// 首頁組件
emitter.on({ eventId: EVENT_CART_UPDATED }, (data) => {
  this.refreshRecommendations();
});

// 消息中心
emitter.on({ eventId: EVENT_CART_UPDATED }, (data) => {
  this.checkPromotions();
});

三個組件同時監(jiān)聽購物車更新事件,emit一次,三個都收到。這是@Prop/@Link做不到的——它們需要組件有明確的層級關(guān)系。

登錄狀態(tài)同步

經(jīng)典場景:登錄成功后,多個頁面需要更新狀態(tài):

const EVENT_USER_STATE_CHANGED = 2001;

// 登錄頁
async login(username: string, password: string): Promise<void> {
  let user = await this.authService.login(username, password);
  AppStorage.setOrCreate('currentUser', user);
  emitter.emit({
    eventId: EVENT_USER_STATE_CHANGED,
    parameters: { isLogin: true, userId: user.id }
  });
}

// 退出登錄
logout(): void {
  this.authService.logout();
  AppStorage.delete('currentUser');
  emitter.emit({
    eventId: EVENT_USER_STATE_CHANGED,
    parameters: { isLogin: false }
  });
}

// 個人中心頁
aboutToAppear(): void {
  emitter.on({ eventId: EVENT_USER_STATE_CHANGED }, this.onUserStateChanged);
}

private onUserStateChanged = (data: emitter.EventData) => {
  let isLogin = data.parameters['isLogin'] as boolean;
  if (isLogin) {
    this.showUserInfo();
  } else {
    this.showLoginButton();
  }
}

為什么不直接用AppStorage? AppStorage的數(shù)據(jù)變化會觸發(fā)UI刷新,但不會觸發(fā)邏輯回調(diào)。比如退出登錄時你不僅要更新UI,還要清理緩存、取消請求、重置狀態(tài)——這些是業(yè)務(wù)邏輯,不是UI渲染。Emitter可以觸發(fā)這些邏輯回調(diào)。

封裝事件總線

直接用emitter的API有點(diǎn)啰嗦。封裝一個類型安全的事件總線:

class EventBus {
  private static instance: EventBus;
  private handlers: Map<number, emitter.Callback[]> = new Map();
  static getInstance(): EventBus {
    if (!EventBus.instance) {
      EventBus.instance = new EventBus();
    }
    return EventBus.instance;
  }
  on(eventId: number, callback: emitter.Callback): void {
    let existing = this.handlers.get(eventId);
    if (existing === undefined) {
      existing = [];
    }
    existing.push(callback);
    this.handlers.set(eventId, existing);
    emitter.on({ eventId: eventId }, callback);
  }
  off(eventId: number, callback: emitter.Callback): void {
    let existing = this.handlers.get(eventId);
    if (existing !== undefined) {
      let newList: emitter.Callback[] = [];
      for (let i = 0; i < existing.length; i++) {
        if (existing[i] !== callback) {
          newList.push(existing[i]);
        }
      }
      this.handlers.set(eventId, newList);
    }
    emitter.off(eventId, callback);
  }
  emit(eventId: number, parameters?: Record<string, Object>): void {
    if (parameters !== undefined) {
      emitter.emit({ eventId: eventId, parameters: parameters });
    } else {
      emitter.emit({ eventId: eventId });
    }
  }
}

封裝后使用更簡潔:

EventBus.getInstance().on(EVENT_LOGIN, this.onLogin);
EventBus.getInstance().emit(EVENT_LOGIN, { userId: '123' });
EventBus.getInstance().off(EVENT_LOGIN, this.onLogin);

CommonEvent跨應(yīng)用通信

CommonEvent是系統(tǒng)級廣播,可以跨應(yīng)用、跨進(jìn)程:

import { commonEventManager } from '@kit.BasicServicesKit';

// 訂閱系統(tǒng)事件
let subscriber = commonEventManager.createSubscriber({
  events: ['usual.event.SCREEN_OFF']
});

commonEventManager.subscribe(subscriber, (err, data) => {
  // 處理屏幕關(guān)閉事件
});

// 取消訂閱
commonEventManager.unsubscribe(subscriber);

CommonEvent需要權(quán)限聲明。 很多系統(tǒng)事件需要對應(yīng)權(quán)限才能訂閱。自定義事件不需要權(quán)限,但要確保事件名的唯一性——建議用包名作前綴:

let CUSTOM_EVENT = 'com.example.app.ORDER_CREATED';

CommonEvent發(fā)送

發(fā)送自定義CommonEvent:

commonEventManager.publish('com.example.app.ORDER_CREATED', {
  parameters: {
    orderId: '20240101001',
    amount: 99.9
  }
}, (err) => {
  if (!err) {
    console.info('Event published');
  }
});

publish是異步的,回調(diào)在發(fā)送完成后觸發(fā)。

注意:CommonEvent的發(fā)送和接收可以在不同應(yīng)用中。 這意味著你的事件可能被其他應(yīng)用監(jiān)聽到,敏感數(shù)據(jù)不要直接放在parameters里。

什么時候用CommonEvent

CommonEvent的使用場景很明確:

  1. 監(jiān)聽系統(tǒng)事件:屏幕開關(guān)、網(wǎng)絡(luò)變化、時區(qū)變化、電量變化
  2. 跨應(yīng)用通知:支付應(yīng)用通知訂單完成,地圖應(yīng)用接收導(dǎo)航請求
  3. 多進(jìn)程通信:同一個應(yīng)用的UIAbility和ExtensionAbility在不同進(jìn)程

應(yīng)用內(nèi)通信不要用CommonEvent——它比Emitter重得多(涉及跨進(jìn)程序列化),而且有權(quán)限和可見性問題。

時序控制

事件到達(dá)順序不保證。如果A事件必須在B事件之前處理:

const EVENT_STEP1_COMPLETE = 3001;

// A完成后發(fā)事件
emitter.emit({ eventId: EVENT_STEP1_COMPLETE });

// B等A完成才執(zhí)行
emitter.on({ eventId: EVENT_STEP1_COMPLETE }, () => {
  this.executeStep2();
});

用事件鏈代替時序假設(shè)——A完成后emit通知,B監(jiān)聽通知再執(zhí)行。不要假設(shè)emit是同步的。

內(nèi)存泄漏防護(hù)

Emitter的回調(diào)如果不off,組件銷毀后依然執(zhí)行。最危險的情況:

// 危險:匿名函數(shù)無法off
emitter.on({ eventId: 1001 }, (data) => {
  this.updateUI(); // 組件已銷毀,this可能無效
});

// 安全:保存引用,組件銷毀時off
private handler = (data: emitter.EventData) => {
  this.updateUI();
}

aboutToAppear(): void {
  emitter.on({ eventId: 1001 }, this.handler);
}

aboutToDisappear(): void {
  emitter.off(1001, this.handler);
}

規(guī)則:on和off必須成對出現(xiàn),回調(diào)必須是類成員變量,不能是匿名函數(shù)。

踩坑清單

問題原因解決
組件銷毀后crash沒off取消訂閱aboutToDisappear中off
off不掉匿名函數(shù)無法比較引用回調(diào)保存為類成員變量
事件收不到Emitter跨進(jìn)程不生效跨進(jìn)程用CommonEvent
收到重復(fù)事件多次on同一回調(diào)on前先off
事件順序不對emit不保證同步時序用事件鏈傳遞順序
CommonEvent收不到沒聲明權(quán)限檢查module.json5權(quán)限
自定義事件被攔截事件名不夠唯一用包名作前綴
參數(shù)類型丟失parameters反序列化只傳基本類型
數(shù)據(jù)敏感泄露CommonEvent跨應(yīng)用可見敏感數(shù)據(jù)加密或用Emitter
事件總線內(nèi)存增長Map中回調(diào)越積越多off時清理Map記錄

事件總線是組件通信的"后門"——不用它時架構(gòu)清晰,用了它時調(diào)用鏈隱晦。能用@Prop/@Link解決的不要用Emitter,能不跨進(jìn)程的不要用CommonEvent。但當(dāng)通信距離超出組件樹范圍時,事件總線就是最干凈的解法。

總結(jié)

到此這篇關(guān)于HarmonyOS 6.0應(yīng)用級事件總線與組件間通信的文章就介紹到這了,更多相關(guān)HarmonyOS應(yīng)用級事件總線與組件間通信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

阿克| 固阳县| 涟水县| 大洼县| 邢台县| 太白县| 平远县| 建始县| 凭祥市| 册亨县| 桦川县| 尉氏县| 航空| 阿巴嘎旗| 东至县| 鹤庆县| 正定县| 镇赉县| 南岸区| 济阳县| 巫溪县| 沧州市| 临颍县| 霍林郭勒市| 灯塔市| 搜索| 宜君县| 图木舒克市| 乐安县| 开平市| 防城港市| 盐城市| 丰县| 崇信县| 太保市| 行唐县| 合山市| 繁昌县| 溧水县| 三原县| 化德县|