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应用,实现真正的跨设备协同体验。