Harmony开发之公共事件与通知——应用间的沟通桥梁

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 性能优化建议

  1. 合理使用公共事件:避免频繁发布不必要的事件,减少系统负担
  2. 及时退订事件:在不需要时及时退订公共事件,避免内存泄漏
  3. 优化通知频率:避免过于频繁的通知,影响用户体验
  4. 使用适当的重要级别:根据通知内容的重要性选择合适的通知级别
  5. 处理权限拒绝:优雅处理用户拒绝通知权限的情况

八、总结与最佳实践

8.1 核心要点回顾

  1. 公共事件机制:实现应用间后台通信,支持系统事件和自定义事件
  2. 通知机制:向用户提供可视化的消息提醒,支持多种样式和交互
  3. 跨设备协同:通过公共事件实现设备间的数据同步和状态感知
  4. 权限管理:合理申请和使用所需权限,确保应用正常运行

8.2 最佳实践

  1. 场景选择: 需要后台通信时使用公共事件 需要用户交互时使用通知 需要跨设备协同时结合使用两者
  2. 权限申请: 在module.json5中声明所需权限 运行时动态请求用户授权 优雅处理权限拒绝的情况
  3. 性能优化: 避免频繁发布事件和通知 及时清理不再需要的订阅和通知 使用适当的事件类型和通知级别
  4. 用户体验: 提供清晰的通知内容 支持通知操作按钮 合理控制通知频率

通过合理运用公共事件与通知机制,可以构建出响应迅速、体验流畅的HarmonyOS应用,实现真正的跨设备协同体验。


posted @ 2025-12-24 10:38  wrystart  阅读(2)  评论(0)    收藏  举报