Harmony開發之公共事件與通知——應用間的溝通橋樑
引入:跨應用協同的魔法
在日常使用手機時,我們經常會遇到這樣的場景:當Wi-Fi連接成功後,多個應用會同時彈出"網絡已連接"的提示;當收到新消息時,即使應用在後台運行,也能在通知欄看到提醒。這些看似簡單的功能背後,正是HarmonyOS公共事件與通知機制在發揮作用。它們如同應用間的"神經系統",讓不同的應用能夠感知系統狀態變化,實現跨應用的協同工作。
一、公共事件與通知機制概述
1.1 核心概念
**公共事件(Common Event)**是HarmonyOS提供的應用間通信機制,允許應用訂閲系統或其他應用發佈的事件,實現後台的事件驅動通信。公共事件服務(CES)負責事件的訂閲、發佈和退訂管理。
**通知(Notification)**是應用向用户傳遞信息的主要方式,通過通知增強服務(ANS)系統服務來為應用程序提供發佈通知的能力。通知會在狀態欄、通知中心等位置顯示給用户,支持多種樣式和交互操作。
1.2 通信模式對比
|
特性
|
公共事件
|
通知
|
|
通信模式 |
訂閲/發佈:單向、匿名的後台通信
|
點對點/系統託管:面向用户的交互
|
|
可見性 |
後台執行,用户無感知
|
狀態欄、通知中心可見
|
|
主要目的 |
系統內部通信、狀態同步
|
人機交互、信息提醒
|
|
參與者 |
發佈者、訂閲者(應用或系統服務)
|
發佈者(應用)、系統服務、用户
|
二、公共事件開發詳解
2.1 公共事件類型
系統公共事件
系統預定義的事件,由系統服務在狀態變化時發佈,常見的有:
usual.event.SCREEN_OFF(屏幕關閉)usual.event.WIFI_CONNECTED(Wi-Fi已連接)common.event.DEVICE_OFFLINE(設備下線)common.event.PACKAGE_REMOVED(應用包被移除)
自定義公共事件
應用為處理特定業務邏輯而定義的事件,主要用於實現跨進程的事件通信能力。
2.2 公共事件發送方式
無序公共事件
CES轉發事件時不考慮訂閲者接收順序,不保證順序一致性。
有序公共事件
根據訂閲者優先級順序傳遞,高優先級訂閲者可修改事件內容或終止事件傳遞。
粘性公共事件
支持先發布後訂閲,事件會持久化在系統中供後續訂閲者接收。
2.3 核心接口類
公共事件相關基礎類包含:
- CommonEventData:封裝公共事件相關信息
- CommonEventPublishInfo:封裝公共事件發佈相關屬性
- CommonEventSubscribeInfo:封裝公共事件訂閲相關信息
- CommonEventSubscriber:封裝公共事件訂閲者及相關參數
- CommonEventManager:提供訂閲、退訂和發佈公共事件的靜態接口
三、公共事件實戰開發
3.1 訂閲公共事件
import commonEvent from '@ohos.commonEventManager';
import { BusinessError } from '@ohos.BasicServicesKit';
// 訂閲網絡連接變化事件
async function subscribeNetworkEvent(): Promise<void> {
try {
const subscribeInfo = {
events: ["usual.event.network.CONNECTIVITY_CHANGE"]
};
const subscriber = await commonEvent.createSubscriber(subscribeInfo);
commonEvent.subscribe(subscriber, (err: BusinessError, data: commonEvent.CommonEventData) => {
if (err) {
console.error(`訂閲失敗: ${err.code}, ${err.message}`);
return;
}
console.info('收到網絡變化事件');
// 處理網絡狀態變化邏輯
this.handleNetworkChange();
});
} catch (error) {
console.error('創建訂閲者失敗:', error);
}
}
3.2 發佈自定義公共事件
import commonEvent from '@ohos.commonEventManager';
import { BusinessError } from '@ohos.BasicServicesKit';
// 發佈自定義公共事件
async function publishCustomEvent(): Promise<void> {
try {
const options: commonEvent.CommonEventPublishData = {
code: 1,
data: "自定義事件數據"
};
await commonEvent.publish("com.example.MY_CUSTOM_EVENT", options);
console.info('自定義事件發佈成功');
} catch (error) {
console.error('發佈事件失敗:', error);
}
}
// 發佈帶權限的公共事件
async function publishPermissionEvent(): Promise<void> {
try {
const options: commonEvent.CommonEventPublishData = {
code: 1,
data: "帶權限的事件數據",
subscriberPermissions: ["com.example.permission.MY_PERMISSION"]
};
await commonEvent.publish("com.example.PERMISSION_EVENT", options);
console.info('帶權限事件發佈成功');
} catch (error) {
console.error('發佈帶權限事件失敗:', error);
}
}
3.3 發佈有序公共事件
import commonEvent from '@ohos.commonEventManager';
import { BusinessError } from '@ohos.BasicServicesKit';
// 發佈有序公共事件
async function publishOrderedEvent(): Promise<void> {
try {
const options: commonEvent.CommonEventPublishData = {
code: 1,
data: "有序事件數據",
isOrdered: true
};
await commonEvent.publish("com.example.ORDERED_EVENT", options);
console.info('有序事件發佈成功');
} catch (error) {
console.error('發佈有序事件失敗:', error);
}
}
3.4 發佈粘性公共事件
import commonEvent from '@ohos.commonEventManager';
import { BusinessError } from '@ohos.BasicServicesKit';
// 發佈粘性公共事件
async function publishStickyEvent(): Promise<void> {
try {
const options: commonEvent.CommonEventPublishData = {
code: 1,
data: "粘性事件數據",
isSticky: true
};
await commonEvent.publish("com.example.STICKY_EVENT", options);
console.info('粘性事件發佈成功');
} catch (error) {
console.error('發佈粘性事件失敗:', error);
}
}
3.5 權限配置
在module.json5中配置所需權限:
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.COMMONEVENT_STICKY",
"reason": "發佈粘性公共事件",
"usedScene": {
"ability": [".MainAbility"],
"when": "inuse"
}
},
{
"name": "com.example.permission.MY_PERMISSION",
"reason": "自定義權限",
"usedScene": {
"ability": [".MainAbility"],
"when": "inuse"
}
}
]
}
}
四、通知開發詳解
4.1 通知類型
HarmonyOS支持六種通知樣式:
- 普通文本(NOTIFICATION_CONTENT_BASIC_TEXT)
- 長文本(NOTIFICATION_CONTENT_LONG_TEXT)
- 圖片(NOTIFICATION_CONTENT_PICTURE)
- 社交(NOTIFICATION_CONVERSATIONAL_CONTENT)
- 多行文本(NOTIFICATION_MULTILINE_CONTENT)
- 媒體(NOTIFICATION_MEDIA_CONTENT)
4.2 通知重要級別
NotificationSlot的級別支持:
- LEVEL_NONE:通知不發佈
- LEVEL_MIN:通知可以發佈,但不顯示在通知欄
- LEVEL_LOW:通知顯示在通知欄,不自動彈出
- LEVEL_DEFAULT:通知顯示在通知欄,觸發提示音
- LEVEL_HIGH:通知顯示在通知欄,自動彈出,觸發提示音
4.3 核心接口類
通知相關基礎類包含:
- NotificationSlot:設置提示音、振動、鎖屏顯示和重要級別
- NotificationRequest:設置具體的通知對象
- NotificationHelper:封裝發佈、更新、刪除通知等靜態方法
五、通知實戰開發
5.1 創建通知渠道
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 創建通知渠道
async function createNotificationSlot(): Promise<void> {
try {
const slot = {
id: "slot_001",
name: "默認通知渠道",
level: notificationManager.SlotLevel.LEVEL_DEFAULT,
enableVibration: true,
lockscreenVisibleness: notificationManager.VisibilityType.VISIBILITY_TYPE_PUBLIC,
enableLight: true,
ledLightColor: Color.RED
};
await notificationManager.addSlot(slot);
console.info('通知渠道創建成功');
} catch (error) {
console.error('創建通知渠道失敗:', error);
}
}
5.2 發佈普通文本通知
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 發佈普通文本通知
async function publishTextNotification(): Promise<void> {
try {
const notificationRequest: notificationManager.NotificationRequest = {
id: 1,
slotId: "slot_001",
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: "通知標題",
text: "這是通知內容",
additionalText: "附加信息"
}
},
badgeNumber: 1,
autoDeletedTime: Date.now() + 3000 // 3秒後自動刪除
};
await notificationManager.publish(notificationRequest);
console.info('文本通知發佈成功');
} catch (error) {
console.error('發佈通知失敗:', error);
}
}
5.3 發佈圖片通知
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';
// 發佈圖片通知
async function publishImageNotification(): Promise<void> {
try {
// 獲取圖片資源
const resourceManager = getContext().resourceManager;
const imageArray = await resourceManager.getMediaContent($r('app.media.notification_icon').id);
const imageResource = image.createImageSource(imageArray.buffer);
const imagePixelMap = await imageResource.createPixelMap();
const notificationRequest: notificationManager.NotificationRequest = {
id: 2,
slotId: "slot_001",
largeIcon: imagePixelMap,
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE,
picture: {
title: "圖片通知",
text: "這是一張圖片通知",
bigPicture: imagePixelMap
}
}
};
await notificationManager.publish(notificationRequest);
console.info('圖片通知發佈成功');
} catch (error) {
console.error('發佈圖片通知失敗:', error);
}
}
5.4 發佈進度條通知
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { request } from '@kit.NetworkKit';
// 發佈進度條通知
async function publishProgressNotification(): Promise<void> {
try {
const notificationRequest: notificationManager.NotificationRequest = {
id: 3,
slotId: "slot_001",
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: "文件下載",
text: "正在下載文件..."
}
},
template: {
name: 'downloadTemplate',
data: {
title: 'PDF文件下載',
fileName: 'test.pdf',
progressValue: 0
}
}
};
// 發佈初始通知
await notificationManager.publish(notificationRequest);
// 模擬下載進度更新
let progress = 0;
const interval = setInterval(async () => {
progress += 10;
if (progress > 100) {
clearInterval(interval);
return;
}
// 更新通知
notificationRequest.template!.data!.progressValue = progress;
notificationRequest.template!.data!.fileName = `test.pdf 下載進度: ${progress}%`;
await notificationManager.publish(notificationRequest);
}, 1000);
} catch (error) {
console.error('發佈進度通知失敗:', error);
}
}
5.5 帶操作按鈕的通知
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { WantAgent } from '@kit.AbilityKit';
// 發佈帶操作按鈕的通知
async function publishActionNotification(): Promise<void> {
try {
// 創建WantAgent(點擊通知後要觸發的意圖)
const wantAgentInfo: WantAgent.WantAgentInfo = {
wants: [
{
bundleName: 'com.example.myapp',
abilityName: 'MainAbility'
}
],
actionType: WantAgent.OperationType.START_ABILITIES,
requestCode: 0
};
const wantAgent = await WantAgent.getWantAgent(wantAgentInfo);
const notificationRequest: notificationManager.NotificationRequest = {
id: 4,
slotId: "slot_001",
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: "帶操作的通知",
text: "點擊查看詳情或執行操作"
}
},
wantAgent: wantAgent,
actionButtons: [
{
title: '打開應用',
wantAgent: wantAgent
},
{
title: '取消',
wantAgent: {
wants: [
{
action: 'com.example.CANCEL_ACTION'
}
],
actionType: WantAgent.OperationType.SEND_COMMON_EVENT
}
}
]
};
await notificationManager.publish(notificationRequest);
console.info('帶操作按鈕的通知發佈成功');
} catch (error) {
console.error('發佈帶操作通知失敗:', error);
}
}
5.6 取消通知
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 取消指定通知
async function cancelNotification(): Promise<void> {
try {
await notificationManager.cancelNotification(1);
console.info('通知取消成功');
} catch (error) {
console.error('取消通知失敗:', error);
}
}
// 取消所有通知
async function cancelAllNotifications(): Promise<void> {
try {
await notificationManager.cancelAllNotifications();
console.info('所有通知取消成功');
} catch (error) {
console.error('取消所有通知失敗:', error);
}
}
5.7 權限申請
在發佈通知前,需要申請通知權限:
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 請求通知權限
async function requestNotificationPermission(): Promise<void> {
try {
await notificationManager.requestEnableNotification();
console.info('通知權限申請成功');
} catch (error) {
if (error.code === 1600004) {
console.info('用户拒絕了通知權限');
} else {
console.error('請求通知權限失敗:', error);
}
}
}
六、綜合實戰:跨設備文件傳輸
6.1 場景描述
用户在手機上啓動文件發送操作,將大文件發送到同一賬號下的平板電腦。平板上的文件傳輸應用在後台訂閲自定義公共事件,當文件傳輸完成後,手機應用發佈公共事件,平板應用接收事件並處理文件,最後通過通知告知用户。
6.2 代碼實現
手機端(發送方)
import commonEvent from '@ohos.commonEventManager';
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 文件傳輸完成後發佈公共事件
async function publishFileTransferComplete(filePath: string): Promise<void> {
try {
const options: commonEvent.CommonEventPublishData = {
code: 200,
data: JSON.stringify({
filePath: filePath,
fileName: "test.pdf",
fileSize: "10MB",
transferTime: new Date().toISOString()
}),
isOrdered: true
};
await commonEvent.publish("com.example.FILE_TRANSFER_COMPLETE", options);
console.info('文件傳輸完成事件發佈成功');
// 發送本地通知
await publishTransferCompleteNotification();
} catch (error) {
console.error('發佈文件傳輸事件失敗:', error);
}
}
// 發佈傳輸完成通知
async function publishTransferCompleteNotification(): Promise<void> {
try {
const notificationRequest: notificationManager.NotificationRequest = {
id: 1001,
slotId: "slot_transfer",
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: "文件傳輸完成",
text: "文件已成功發送到平板設備"
}
}
};
await notificationManager.publish(notificationRequest);
} catch (error) {
console.error('發佈通知失敗:', error);
}
}
平板端(接收方)
import commonEvent from '@ohos.commonEventManager';
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 訂閲文件傳輸完成事件
async function subscribeFileTransferEvent(): Promise<void> {
try {
const subscribeInfo = {
events: ["com.example.FILE_TRANSFER_COMPLETE"]
};
const subscriber = await commonEvent.createSubscriber(subscribeInfo);
commonEvent.subscribe(subscriber, async (err: BusinessError, data: commonEvent.CommonEventData) => {
if (err) {
console.error(`訂閲失敗: ${err.code}, ${err.message}`);
return;
}
// 解析事件數據
const eventData = JSON.parse(data.data as string);
console.info('收到文件傳輸完成事件:', eventData);
// 處理文件
await handleFileTransfer(eventData);
});
} catch (error) {
console.error('創建訂閲者失敗:', error);
}
}
// 處理文件傳輸
async function handleFileTransfer(eventData: any): Promise<void> {
try {
// 模擬文件處理邏輯
console.info(`開始處理文件: ${eventData.fileName}`);
// 模擬文件處理耗時
await new Promise(resolve => setTimeout(resolve, 2000));
console.info('文件處理完成');
// 發送處理完成通知
await publishFileProcessedNotification(eventData.fileName);
} catch (error) {
console.error('處理文件失敗:', error);
}
}
// 發佈文件處理完成通知
async function publishFileProcessedNotification(fileName: string): Promise<void> {
try {
const notificationRequest: notificationManager.NotificationRequest = {
id: 2001,
slotId: "slot_transfer",
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: "文件接收完成",
text: `${fileName} 已保存至下載文件夾`
}
}
};
await notificationManager.publish(notificationRequest);
console.info('文件處理完成通知發佈成功');
} catch (error) {
console.error('發佈通知失敗:', error);
}
}
七、調試與優化
7.1 調試工具
HarmonyOS提供了調試助手工具,幫助開發者調試公共事件和通知:
- CEM調試助手:用於調試公共事件
- ANM調試助手:用於調試通知
7.2 性能優化建議
- 合理使用公共事件:避免頻繁發佈不必要的事件,減少系統負擔
- 及時退訂事件:在不需要時及時退訂公共事件,避免內存泄漏
- 優化通知頻率:避免過於頻繁的通知,影響用户體驗
- 使用適當的重要級別:根據通知內容的重要性選擇合適的通知級別
- 處理權限拒絕:優雅處理用户拒絕通知權限的情況
八、總結與最佳實踐
8.1 核心要點回顧
- 公共事件機制:實現應用間後台通信,支持系統事件和自定義事件
- 通知機制:向用户提供可視化的消息提醒,支持多種樣式和交互
- 跨設備協同:通過公共事件實現設備間的數據同步和狀態感知
- 權限管理:合理申請和使用所需權限,確保應用正常運行
8.2 最佳實踐
- 場景選擇: 需要後台通信時使用公共事件 需要用户交互時使用通知 需要跨設備協同時結合使用兩者
- 權限申請: 在
module.json5中聲明所需權限 運行時動態請求用户授權 優雅處理權限拒絕的情況 - 性能優化: 避免頻繁發佈事件和通知 及時清理不再需要的訂閲和通知 使用適當的事件類型和通知級別
- 用户體驗: 提供清晰的通知內容 支持通知操作按鈕 合理控制通知頻率
通過合理運用公共事件與通知機制,可以構建出響應迅速、體驗流暢的HarmonyOS應用,實現真正的跨設備協同體驗。