HarmonyOS 公共事件機制介紹以及多進程之間的通信
CES(Common Event Service,公共事件服務)為應用程序提供訂閱、發布、退訂公共事件的能力
1. 公共事件的介紹
1.1.1公共事件的分類:公共事件從系統的角度可以分為系統公共事件和自定義公共事件
-
系統公共事件:CES內部定義的公共事件,當前僅支持系統應用和系統服務發布,例如HAP安裝、更新、卸載等公共事件。目前支持的系統公共事件請參考系統公共事件列表
-
自定義的公共事件:應用定義的公共事件,可用于實現跨進程的事件通信能力(重點:可以死實現跨進程通信)
1.1.2 公共事件的發送方式
-
無序公共事件:CES在轉發公共事件時,不考慮訂閱者是否接收到該事件,也不保證訂閱者接收到該事件的順序與其訂閱順序一致。打個比方就好像群發短信,廣播式發送,一次性發給所有訂閱者,不保證順序,誰先收到看運氣,不保證送達,可能都有接收失敗的情況。比如老師給全班群發短信,同學A手機信號好,收到信息的時間是13:05,而B同學的手機比較卡頓,收到信息的時間為13:07。
-
代碼示例
// 發布無序事件 CommonEventData event = new CommonEventData("MSG_UPDATE"); CommonEventManager.publishCommonEvent(event);
-
適用場景
- 非關鍵性的通知(如:APP內容更新提醒)
- 不需要反饋的廣播(如定時任務的觸發)
-
-
有序公共事件:CES在轉發公共事件時,根據訂閱者設置的優先級等級,優先將公共事件發送給優先級較高的訂閱者,等待其成功接收該公共事件之后再將事件發送給優先級較低的訂閱者。如果有多個訂閱者具有相同的優先級,則他們將隨機接收到公共事件。打個比方就好像銀行的呼叫系統,嚴格按照優先級,高優先級先處理,前一個處理完才傳下一個,鏈式傳遞,任一環節可終止傳遞,就好比商場的vip服務,鉆石客戶優先辦理。
-
代碼示例
// 訂閱時設置優先級 CommonEventSubscribeInfo info = new CommonEventSubscribeInfo(); info.setPriority(9); // 最高優先級CommonEventSubscriber subscriber = new CommonEventSubscriber(info) {@Overridepublic void onReceiveEvent(CommonEventData event) {// 處理完成后必須調用此方法才會傳遞給下一級!finishCommonEvent();} };
-
適用場景
- 需要審核流程的操作(比如付款確認鏈)
- 敏感操作驗證(多層安全校驗)
-
-
粘性公共事件:能夠讓訂閱者收到在訂閱前已經發送的公共事件就是粘性公共事件。普通的公共事件只能在訂閱后發送才能收到,而粘性公共事件的特殊性就是可以先發送后訂閱,同時也支持先訂閱后發送。發送粘性事件必須是系統應用或系統服務,粘性事件發送后會一直存在系統中,且發送者需要申請ohos.permission.COMMONEVENT_STICKY權限,配置方式請參見聲明權限。打個比方來說就像小區公告欄貼出的停水通知,居民A當天看到,就可以馬上儲水,居民B三天后搬來,看到歷史通知,只有物業(系統應用)有權粘貼
- 代碼
// 發布粘性事件(需系統權限) const commonEvent = {name: "CONNECTIVITY_CHANGE", // 網絡狀態變更事件sticky: true, // 設為粘性事件data: { connected: false } }; commonEventManager.publishAsStickyCommonEvent(commonEvent);// 新用戶安裝APP后訂閱 commonEventManager.subscribe({name: "CONNECTIVITY_CHANGE" }, (err, data) => {console.log("當前網絡狀態:" + data.connected);// 新訂閱者立即收到最后一次網絡狀態! });
- 適用場景
- 系統狀態通知(比如電池電量、網絡連接)
- 全局配置變更(比如深色模式切換)
1.2.3 關鍵對比
特性 | 無序事件 | 有序事件 | 粘性事件 |
---|---|---|---|
接收順序 | 隨機 | 按優先級 | 按訂閱順序 |
事件存續 | 瞬態 | 瞬態 | 持久化存儲 |
是否可中斷 | 否 | ? 可中斷鏈 | 否 |
發布權限 | 所有應用 | 所有應用 | 僅系統應用 |
后訂閱收歷史事件 | 否 | 否 | ? 能接收到 |
典型用例 | 通知推送 | 流程審批 | 系統狀態同步 |
1.2.4 為什么黏性事件需要特殊權限
-
安全分險
- 粘性事件長期駐留內存
- 可能包含敏感信息(如網絡狀態)
-
資源消耗
```mermaid graph LRA[持久化存儲] --> B[內存占用]A --> C[磁盤空間]
-
權限申請: 在
module.json5
中添加:
"requestPermissions": [{"name": "ohos.permission.COMMONEVENT_STICKY"}
]
2. 公共事件的運作機制
每個應用都可以按需訂閱公共事件,訂閱成功,當公共事件發布時,系統會將其發送給對應的應用。這些公共事件可能來自系統,其他應用和應用自身
2.1 安全注意事項
-
公共事件發布方
:如果不加限制,任何應用都可以訂閱公共事件并讀取相關信息,應避免在公共事件中攜帶敏感信息。采用以下方式,可以限制公共事件接收方的范圍。
- 通過CommonEventPublishData中的subscriberPermissions參數指定訂閱者所需權限。
- 通過CommonEventPublishData中的bundleName參數指定訂閱者的包名。
-
公共事件訂閱方
:訂閱自定義公共事件后,任意應用都可以向訂閱者發送潛在的惡意公共事件。采用以下方式,可以限制公共事件發布方的范圍。
- 通過CommonEventSubscribeInfo中的publisherPermission參數指定發布者所需權限。
- 通過CommonEventSubscribeInfo中的publisherBundleName參數參數指定發布者的包名。
-
自定義公共事件名稱應確保全局唯一,否則可能與其他公共事件沖突。
3. 動態訂閱公共事件
3.1 場景介紹
動態訂閱是指當應用在運行狀態時對某個公共事件進行訂閱,在運行期間如果有訂閱的事件發布那么訂閱了這個事件的應用將會收到改事件及其傳遞的參數
例如,某應用希望在運行期間收到電量過低的事件,并根據該事件降低其運行功耗,那么該應用便可動態訂閱電量過低事件,收到該事件關閉一些非必要的任務來降低功耗
- 定義:應用程序在運行時訂閱特定公共事件的機制
- 特點:僅在應用處于前臺運行狀態時有效(后臺不接受事件)
- 生命周期:從訂閱開始到應用進程結束/主動退訂
3.2 工作流程圖展示
3.3 完整開發流程概覽
4. 公共事件的發布和訂閱的使用步驟
4.1.1 公共事件的發布
當需要發布某個自定義公共事件時,可以通過publish()方法發布事件。發布的公共事件可以攜帶數據,供訂閱者解析并進行下一步處理。
1. 簡單事件發布(無數據)
// 導入必要模塊
import { commonEventManager } from '@kit.BasicServicesKit';// 發布"任務完成"通知
commonEventManager.publish('TASK_COMPLETED', (err) => {if (err) {console.error('發布失敗', err)} else {console.log('任務完成通知已發送')}
});
2. 傳遞數據的事件發布(帶溫度信息)
import { commonEventManager } from '@kit.BasicServicesKit';// 準備事件數據
const eventData = {code: 200, // 溫度事件代碼data: "23.5°C", isSticky: true // 新訂閱者也應知道當前溫度
};// 發布帶溫度的天氣事件
commonEventManager.publish('TEMPERATURE_UPDATE', eventData, (err) => {if (err) {console.error('溫度發布失敗', err)} else {console.log('當前溫度已更新')}
});
4.1.2 公共事件的訂閱
場景介紹
動態訂閱是指當應用在運行狀態時對某個公共事件進行訂閱,在運行期間如果有訂閱的事件發布那么訂閱了這個事件的應用將會收到該事件及其傳遞的參數。
例如,某應用希望在其運行期間收到電量過低的事件,并根據該事件降低其運行功耗,那么該應用便可動態訂閱電量過低事件,收到該事件后關閉一些非必要的任務來降低功耗。
訂閱部分系統公共事件需要先申請權限,訂閱這些事件所需要的權限請見公共事件權限列表
從鴻蒙系統服務模塊導入commonEventManager
,這是鴻蒙提供的事件管理工具,類似于瀏覽器的EventBus或Node.js的EventEmitter
class SubscribeClass {subscriber?: commonEventManager.CommonEventSubscriber
使用步驟
1.創建事件訂閱類
- 定義
SubscribeClass
類作為一個事件中心 - 聲明
subscriber
屬性 - 用來存儲訂閱者ID(就像你的訂閱憑證)
subscribe(event: string, callBack: (value: string) => void) {this.subscriber = commonEventManager.createSubscriberSync({ events: [event] })commonEventManager.subscribe(this.subscriber, (err, value) => {callBack(value.data as string)})
}
2.訂閱功能詳解
就像訂閱雜志:
createSubscriberSync({ events: [event] })
→ 告訴系統"我想訂閱這本周刊"(注冊訂閱者)commonEventManager.subscribe(...)
→ 實際訂閱動作(err, value) => { callBack(...) }
→ 當新期刊發布,系統通知你(回調) → 你收到后做處理(執行你提供的callBack函數)
3.調用示例:
subscriberClass.subscribe('NEWS_UPDATE', (content) => {console.log('收到新聞:', content)
})
publish(event: string, data: string = '') {commonEventManager.publish(event, { data }, () => {})
}
5.HarmonyOS 公共事件機制實現卡片通信
在 HarmonyOS 中,卡片(Widget)是輕量化的 UI 組件,而公共事件機制是實現卡片與應用主程序之間通信的核心橋梁。讓我通過一個天氣預報卡片的完整案例來解釋
1.主應用向卡片發送數據(當應用主程序需要更新卡片顯示時)
// 在應用主程序中
import { commonEventManager } from '@kit.BasicServicesKit';// 準備要發送的天氣數據
const weatherData = {city: "北京",temp: "26℃",condition: "晴"
};// 發布帶數據的公共事件
commonEventManager.publish('WEATHER_CARD_UPDATE', { data: JSON.stringify(weatherData), isOrdered: false
}, (err) => {if (err) {console.error('天氣更新失敗');} else {console.log('卡片天氣信息已更新');}
});
2.卡片向主應用發送請求
卡片需要主動請求更新數據時(如用戶刷新按鈕)
// 在卡片服務提供者(CardProvider)中
import { commonEventManager } from '@kit.BasicServicesKit';
import { formHost } from '@kit.FormKit';export class WeatherCardProvider extends FormExtensionAbility {// 處理卡片刷新操作按鈕onFormEvent(formId: string, message: string) {if (message === 'refresh') {// 發布請求刷新事件commonEventManager.publish('REQUEST_WEATHER_UPDATE', { formId }, (err) => {} // 回調省略);}}// 接收主應用更新(在CardProvider中訂閱事件)setupEvents() {commonEventManager.subscribe('WEATHER_CARD_UPDATE', (err, event) => {if (!err) {const data = JSON.parse(event.data);// 更新卡片UIformHost.updateForm(formId, {temperature: `${data.temp}`,weatherIcon: this.getWeatherIcon(data.condition)});}});}
}
3.主應用訂閱卡片請求事件
// 在應用主程序入口(如EntryAbility)
import { BusinessError, commonEventManager } from '@kit.BasicServicesKit';
import { featureAbility } from '@kit.AbilityKit';export default class EntryAbility extends Ability {onCreate() {// 訂閱卡片數據請求事件commonEventManager.subscribe('REQUEST_WEATHER_UPDATE', (err: BusinessError, event) => {if (!err) {// 調用天氣API獲取最新數據this.fetchWeatherData().then(latestData => {// 更新指定卡片this.updateSpecificCard(event.formId, latestData);});}});}// API獲取最新天氣數據private fetchWeatherData(): Promise<WeatherData> {/* API實現省略 */}// 更新指定卡片private updateSpecificCard(formId: string, data: WeatherData) {// 發布更新事件(帶卡片ID)commonEventManager.publish('UPDATE_CARD_' + formId, {data: JSON.stringify(data)});}
}