公共事件与通知——实现HarmonyOS应用间通信

1 公共事件与通知机制概述

在HarmonyOS应用生态中,公共事件与通知机制是实现应用间通信和设备间协同的重要基础。公共事件机制允许应用订阅系统或其他应用发布的事件,实现后台的事件驱动通信;而通知机制则专注于向用户提供可视化的消息提醒和交互接口。

1.1 核心架构组件

HarmonyOS通过两大系统服务实现事件通信能力:

  • CES(公共事件服务):提供事件的订阅、发布和退订能力,支持系统公共事件和自定义公共事件。
  • ANS(高级通知服务):负责通知的发布、管理和显示,支持多种通知样式和交互操作。

1.2 通信模式对比

特性 公共事件 通知
通信模式 订阅/发布:单向、匿名的后台通信 点对点/系统托管:面向用户的交互
可见性 后台执行,用户无感知 状态栏、通知中心可见
主要目的 系统内部通信、状态同步 人机交互、信息提醒
参与者 发布者、订阅者(应用或系统服务) 发布者(应用)、系统服务、用户

2 公共事件机制详解

2.1 公共事件类型

公共事件按来源分为两大类:

2.1.1 系统公共事件

系统预定义的事件,由系统服务在状态变化时发布,常见的有:

  • usual.event.SCREEN_OFF(屏幕关闭)
  • usual.event.WIFI_CONNECTED(Wi-Fi已连接)
  • common.event.DEVICE_OFFLINE(设备下线)
  • common.event.PACKAGE_REMOVED(应用包被移除)

2.1.2 自定义公共事件

应用为处理特定业务逻辑而定义的事件,主要用于实现跨进程的事件通信能力

2.2 公共事件的发送方式

公共事件按发送方式可分为三种类型:

2.2.1 无序公共事件

CES转发事件时不考虑订阅者接收顺序,不保证顺序一致性。

2.2.2 有序公共事件

根据订阅者优先级顺序传递,高优先级订阅者可修改事件内容或终止事件传递。

2.2.3 粘性公共事件

支持先发布后订阅,事件会持久化在系统中供后续订阅者接收。

2.3 公共事件核心API

公共事件相关的基础类构成了完整的事件处理体系:

  • CommonEventData:封装公共事件相关信息
  • CommonEventPublishInfo:设置事件发布属性(有序、粘性等)
  • CommonEventSubscribeInfo:配置订阅者信息和优先级
  • CommonEventSubscriber:处理事件接收回调
  • CommonEventManager:提供订阅、发布、退订的静态接口

3 实战:公共事件的订阅与发布

3.1 订阅公共事件

以下是一个完整的公共事件订阅示例,演示如何监听网络变化事件:

import commonEventManager from '@ohos.commonEventManager';
import Base from '@ohos.base';
import hilog from '@ohos.hilog';

const TAG = 'CommonEventDemo';
const DOMAIN_NUMBER = 0xFF00;

// 用于保存订阅者对象
let subscriber: commonEventManager.CommonEventSubscriber | null = null;

// 订阅者信息配置
let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
  events: ['usual.event.network.CONNECTIVITY_CHANGE'] // 订阅网络连接变化事件
};

// 创建订阅者
commonEventManager.createSubscriber(subscribeInfo, (err: Base.BusinessError, data: commonEventManager.CommonEventSubscriber) => {
  if (err) {
    hilog.error(DOMAIN_NUMBER, TAG, `创建订阅者失败: code=${err.code}, message=${err.message}`);
    return;
  }
  
  hilog.info(DOMAIN_NUMBER, TAG, '成功创建订阅者');
  subscriber = data;
  
  // 订阅公共事件
  subscribeToEvent();
});

// 执行订阅
function subscribeToEvent(): void {
  if (subscriber === null) {
    hilog.error(DOMAIN_NUMBER, TAG, '订阅者未创建');
    return;
  }
  
  commonEventManager.subscribe(subscriber, (err: Base.BusinessError, data: commonEventManager.CommonEventData) => {
    if (err) {
      hilog.error(DOMAIN_NUMBER, TAG, `订阅失败: code=${err.code}, message=${err.message}`);
      return;
    }
    
    hilog.info(DOMAIN_NUMBER, TAG, '接收到网络变化事件');
    // 处理网络变化逻辑
    handleNetworkChange(data);
  });
}

// 处理网络变化事件
function handleNetworkChange(eventData: commonEventManager.CommonEventData): void {
  // 在这里实现具体的业务逻辑
  // 例如检查当前网络类型并更新应用状态
  hilog.info(DOMAIN_NUMBER, TAG, '处理网络状态变化事件');
}

// 退订公共事件
function unsubscribeEvent(): void {
  if (subscriber !== null) {
    commonEventManager.unsubscribe(subscriber, (err: Base.BusinessError) => {
      if (err) {
        hilog.error(DOMAIN_NUMBER, TAG, `退订失败: ${JSON.stringify(err)}`);
      } else {
        hilog.info(DOMAIN_NUMBER, TAG, '成功退订公共事件');
        subscriber = null;
      }
    });
  }
}

3.2 发布公共事件

以下示例演示如何发布自定义公共事件:

import commonEventManager from '@ohos.commonEventManager';
import Base from '@ohos.base';
import hilog from '@ohos.hilog';

const TAG = 'CommonEventDemo';
const DOMAIN_NUMBER = 0xFF00;

// 发布无序公共事件
function publishDisorderedEvent(eventName: string, eventData?: string): void {
  let options: commonEventManager.CommonEventPublishData = {
    code: 0,
    data: eventData || '默认事件数据'
  };
  
  commonEventManager.publish(eventName, options, (err: Base.BusinessError) => {
    if (err) {
      hilog.error(DOMAIN_NUMBER, TAG, `发布事件失败: ${JSON.stringify(err)}`);
    } else {
      hilog.info(DOMAIN_NUMBER, TAG, '成功发布无序公共事件');
    }
  });
}

// 发布有序公共事件(需要权限)
function publishOrderedEvent(eventName: string): void {
  let options: commonEventManager.CommonEventPublishData = {
    code: 1,
    data: '有序事件数据',
    bundleName: 'com.example.publisher' // 指定订阅者包名
  };
  
  commonEventManager.publish(eventName, options, (err: Base.BusinessError) => {
    if (err) {
      hilog.error(DOMAIN_NUMBER, TAG, `发布有序事件失败: ${JSON.stringify(err)}`);
    } else {
      hilog.info(DOMAIN_NUMBER, TAG, '成功发布有序公共事件');
    }
  });
}

// 实际使用示例
publishDisorderedEvent('com.example.custom.EVENT_1', '自定义事件数据');

3.3 权限配置

发布某些类型的公共事件需要在module.json5中配置相应权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.COMMON_EVENT_STICKY",
        "reason": "发布粘性事件所需权限",
        "usedScene": {
          "abilities": [".MainAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.INTERNET",
        "reason": "网络访问权限",
        "usedScene": {
          "abilities": [".MainAbility"],
          "when": "always"
        }
      }
    ]
  }
}

4 通知机制详解

4.1 通知的基本结构

HarmonyOS通知支持多种样式,创建通知时必须包含一种样式:

  • 普通文本样式:基本的标题和内容
  • 长文本样式:支持多行文本内容
  • 图片样式:包含图标或大图
  • 社交样式:聊天类应用的通知
  • 多行文本样式:支持段落式内容
  • 媒体样式:音乐、视频播放控制

4.2 发布通知实战

以下是一个完整的通知发布示例:

import notificationManager from '@ohos.notificationManager';
import WantAgent, { WantAgentInfo, Want } from '@ohos.app.ability.wantAgent';
import Base from '@ohos.base';
import hilog from '@ohos.hilog';

const TAG = 'NotificationDemo';
const DOMAIN_NUMBER = 0xFF00;

// 请求通知权限
async function requestNotificationPermission(): Promise<void> {
  try {
    await notificationManager.requestEnableNotification();
    hilog.info(DOMAIN_NUMBER, TAG, '通知权限已获取');
  } catch (err) {
    hilog.error(DOMAIN_NUMBER, TAG, `通知权限获取失败: ${JSON.stringify(err)}`);
  }
}

// 创建WantAgent用于通知点击行为
async function createWantAgent(): Promise<WantAgent> {
  let wantAgentInfo: WantAgentInfo = {
    wants: [
      {
        bundleName: 'com.example.myapp',
        abilityName: 'EntryAbility',
      } as Want
    ],
    operationType: WantAgent.OperationType.START_ABILITY,
    requestCode: 0,
    wantAgentFlags: [WantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
  };

  return WantAgent.getWantAgent(wantAgentInfo);
}

// 发布基础通知
async function publishBasicNotification(title: string, text: string): Promise<void> {
  try {
    let wantAgent = await createWantAgent();
    
    let notificationRequest: notificationManager.NotificationRequest = {
      id: 1,
      content: {
        contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: title,
          text: text,
          additionalText: '附加文本'
        }
      },
      // 快捷操作按钮
      actionButtons: [
        {
          title: '打开',
          wantAgent: wantAgent
        },
        {
          title: '取消',
          wantAgent: wantAgent
        }
      ],
      tapDismissed: true, // 点击后消失
      autoDeleted: true, // 点击后自动删除
      deliveryTime: new Date(), // 发送时间
      notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION
    };

    await notificationManager.publish(notificationRequest);
    hilog.info(DOMAIN_NUMBER, TAG, '通知发布成功');
  } catch (err) {
    const error = err as Base.BusinessError;
    hilog.error(DOMAIN_NUMBER, TAG, `通知发布失败: code=${error.code}, message=${error.message}`);
  }
}

// 发布带有进度的通知
async function publishProgressNotification(title: string, progress: number, maxProgress: number): Promise<void> {
  let notificationRequest: notificationManager.NotificationRequest = {
    id: 2,
    content: {
      contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
      normal: {
        title: title,
        text: `进度: ${progress}/${maxProgress}`,
        additionalText: `${Math.round((progress / maxProgress) * 100)}%`
      }
    },
    // 进度条设置
    progressBar: {
      value: progress,
      total: maxProgress
    }
  };

  try {
    await notificationManager.publish(notificationRequest);
    hilog.info(DOMAIN_NUMBER, TAG, '进度通知发布成功');
  } catch (err) {
    const error = err as Base.BusinessError;
    hilog.error(DOMAIN_NUMBER, TAG, `进度通知发布失败: ${error.message}`);
  }
}

// 使用示例
requestNotificationPermission();
publishBasicNotification('下载完成', '文件《HarmonyOS开发指南.pdf》已下载完毕');

5 跨应用通信实战案例

5.1 跨设备文件传输场景

以下示例展示了一个完整的跨设备文件传输场景,结合了公共事件和通知机制:

import commonEventManager from '@ohos.commonEventManager';
import notificationManager from '@ohos.notificationManager';
import Base from '@ohos.base';
import hilog from '@ohos.hilog';

const TAG = 'CrossAppCommunication';
const DOMAIN_NUMBER = 0xFF00;

// 自定义事件类型
const CUSTOM_EVENTS = {
  FILE_INCOMING: 'com.example.filetransfer.FILE_INCOMING',
  FILE_PROCESSED: 'com.example.filetransfer.FILE_PROCESSED'
};

// 在接收端设备上订阅文件到达事件
class FileReceiver {
  private subscriber: commonEventManager.CommonEventSubscriber | null = null;

  // 订阅文件到达事件
  async subscribeToFileEvents(): Promise<void> {
    let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
      events: [CUSTOM_EVENTS.FILE_INCOMING]
    };

    commonEventManager.createSubscriber(subscribeInfo, (err: Base.BusinessError, data: commonEventManager.CommonEventSubscriber) => {
      if (err) {
        hilog.error(DOMAIN_NUMBER, TAG, `创建文件事件订阅者失败: ${err.message}`);
        return;
      }

      this.subscriber = data;
      this.setupEventSubscription();
    });
  }

  private setupEventSubscription(): void {
    if (this.subscriber === null) return;

    commonEventManager.subscribe(this.subscriber, (err: Base.BusinessError, data: commonEventManager.CommonEventData) => {
      if (err) {
        hilog.error(DOMAIN_NUMBER, TAG, `文件事件订阅失败: ${err.message}`);
        return;
      }

      this.handleIncomingFile(data);
    });
  }

  // 处理接收到的文件
  private async handleIncomingFile(eventData: commonEventManager.CommonEventData): Promise<void> {
    const filePath = eventData.parameters?.['filePath'];
    const fileName = eventData.parameters?.['fileName'];
    
    hilog.info(DOMAIN_NUMBER, TAG, `接收到文件: ${fileName}, 路径: ${filePath}`);

    // 处理文件(校验、移动等)
    await this.processFile(filePath, fileName);
    
    // 发布处理完成事件
    this.notifyFileProcessed(fileName);
    
    // 发送用户通知
    await this.showFileReceivedNotification(fileName);
  }

  private async processFile(filePath: string, fileName: string): Promise<void> {
    // 文件处理逻辑
    hilog.info(DOMAIN_NUMBER, TAG, `处理文件: ${fileName}`);
    // 实际实现中可能包含文件校验、移动等操作
  }

  private async notifyFileProcessed(fileName: string): Promise<void> {
    let options: commonEventManager.CommonEventPublishData = {
      code: 0,
      data: `文件${fileName}处理完成`,
      parameters: {
        fileName: fileName,
        processedAt: new Date().toISOString()
      }
    };

    commonEventManager.publish(CUSTOM_EVENTS.FILE_PROCESSED, options, (err: Base.BusinessError) => {
      if (err) {
        hilog.error(DOMAIN_NUMBER, TAG, `发布文件处理事件失败: ${err.message}`);
      }
    });
  }

  private async showFileReceivedNotification(fileName: string): Promise<void> {
    let notificationRequest: notificationManager.NotificationRequest = {
      id: Date.now(), // 使用时间戳作为ID确保唯一性
      content: {
        contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: '文件传输完成',
          text: `文件"${fileName}"已接收并保存至下载文件夹`,
          additionalText: '刚刚'
        }
      },
      deliveryTime: new Date()
    };

    try {
      await notificationManager.publish(notificationRequest);
      hilog.info(DOMAIN_NUMBER, TAG, '文件接收通知发送成功');
    } catch (err) {
      const error = err as Base.BusinessError;
      hilog.error(DOMAIN_NUMBER, TAG, `通知发送失败: ${error.message}`);
    }
  }

  // 清理资源
  cleanup(): void {
    if (this.subscriber !== null) {
      commonEventManager.unsubscribe(this.subscriber, (err: Base.BusinessError) => {
        if (err) {
          hilog.error(DOMAIN_NUMBER, TAG, `退订文件事件失败: ${err.message}`);
        }
      });
      this.subscriber = null;
    }
  }
}

// 在发送端设备上发布文件传输事件
class FileSender {
  // 发送文件传输事件
  async sendFile(filePath: string, fileName: string, targetDevice: string): Promise<void> {
    let options: commonEventManager.CommonEventPublishData = {
      code: 0,
      data: '文件传输事件',
      parameters: {
        filePath: filePath,
        fileName: fileName,
        sourceDevice: '发送设备ID',
        targetDevice: targetDevice,
        timestamp: new Date().toISOString()
      },
      bundleName: 'com.example.filetransfer' // 指定接收方包名
    };

    commonEventManager.publish(CUSTOM_EVENTS.FILE_INCOMING, options, (err: Base.BusinessError) => {
      if (err) {
        hilog.error(DOMAIN_NUMBER, TAG, `发布文件传输事件失败: ${err.message}`);
        this.showErrorNotification('文件发送失败');
      } else {
        hilog.info(DOMAIN_NUMBER, TAG, '文件传输事件发布成功');
        this.showSuccessNotification(fileName);
      }
    });
  }

  private async showSuccessNotification(fileName: string): Promise<void> {
    let notificationRequest: notificationManager.NotificationRequest = {
      id: Date.now(),
      content: {
        contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: '文件发送成功',
          text: `文件"${fileName}"已开始传输`,
          additionalText: '发送中'
        }
      }
    };

    await notificationManager.publish(notificationRequest);
  }

  private async showErrorNotification(errorMessage: string): Promise<void> {
    let notificationRequest: notificationManager.NotificationRequest = {
      id: Date.now(),
      content: {
        contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: '传输错误',
          text: errorMessage,
          additionalText: '错误'
        }
      }
    };

    await notificationManager.publish(notificationRequest);
  }
}

// 使用示例
const fileReceiver = new FileReceiver();
await fileReceiver.subscribeToFileEvents();

const fileSender = new FileSender();
await fileSender.sendFile('/path/to/file.pdf', 'document.pdf', '目标设备ID');

6 安全机制与最佳实践

6.1 权限控制与安全措施

在实现跨应用通信时,必须重视安全性:

// 安全配置示例
class SecureEventManager {
  private allowedEvents: Set<string>;
  private allowedPublishers: Set<string>;

  constructor() {
    this.allowedEvents = new Set(['safe.event.1', 'safe.event.2']);
    this.allowedPublishers = new Set(['trusted.publisher.1', 'trusted.publisher.2']);
  }

  // 验证事件发布权限
  private validateEventPublishing(eventName: string, publisherId: string): boolean {
    if (!this.allowedEvents.has(eventName)) {
      hilog.error(DOMAIN_NUMBER, TAG, `事件类型不被允许: ${eventName}`);
      return false;
    }

    if (!this.allowedPublishers.has(publisherId)) {
      hilog.error(DOMAIN_NUMBER, TAG, `发布者未经授权: ${publisherId}`);
      return false;
    }

    return true;
  }

  // 安全的发布方法
  async publishSecureEvent(eventName: string, data: any, publisherId: string): Promise<boolean> {
    if (!this.validateEventPublishing(eventName, publisherId)) {
      return false;
    }

    // 数据加密处理
    const encryptedData = this.encryptData(data);
    
    let options: commonEventManager.CommonEventPublishData = {
      code: 0,
      data: encryptedData
    };

    return new Promise((resolve) => {
      commonEventManager.publish(eventName, options, (err: Base.BusinessError) => {
        if (err) {
          hilog.error(DOMAIN_NUMBER, TAG, `安全事件发布失败: ${err.message}`);
          resolve(false);
        } else {
          hilog.info(DOMAIN_NUMBER, TAG, '安全事件发布成功');
          resolve(true);
        }
      });
    });
  }

  // 数据加密(示例)
  private encryptData(data: any): string {
    // 实际实现应使用鸿OS安全加密API
    return JSON.stringify(data); // 示例中仅作序列化
  }
}

6.2 性能优化建议

  1. 及时退订:在组件销毁时退订事件
  2. 事件去重:避免重复处理相同事件
  3. 异步处理:耗时操作使用goAsyncCommonEvent()
  4. 内存管理:避免事件回调中的内存泄漏
// 优化示例
class OptimizedEventSubscriber {
  private subscriber: commonEventManager.CommonEventSubscriber | null = null;
  private isSubscribed: boolean = false;

  // 带错误重试的订阅
  async subscribeWithRetry(eventName: string, maxRetries: number = 3): Promise<void> {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        await this.subscribeToEvent(eventName);
        return;
      } catch (err) {
        hilog.warn(DOMAIN_NUMBER, TAG, `订阅失败,第${attempt}次重试`);
        if (attempt === maxRetries) {
          throw err;
        }
        await this.delay(1000 * attempt); // 指数退避
      }
    }
  }

  // 异步事件处理
  private async handleEventAsync(eventData: commonEventManager.CommonEventData): Promise<void> {
    const result = this.subscriber?.goAsyncCommonEvent();
    
    try {
      // 执行耗时操作
      await this.processEventData(eventData);
      result?.finishCommonEvent();
    } catch (err) {
      hilog.error(DOMAIN_NUMBER, TAG, `事件处理失败: ${err.message}`);
      result?.finishCommonEvent();
    }
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

7 总结

公共事件与通知机制是HarmonyOS应用间通信的核心技术,通过本文的详细讲解和实战示例,您应该掌握:

  1. 公共事件机制的理解和运用,包括系统事件和自定义事件
  2. 通知系统的完整使用流程,从权限申请到各种样式通知的发布
  3. 跨应用通信的安全实现方案,包括权限控制和数据验证
  4. 实战场景的应用能力,能够解决实际开发中的通信需求

这些技术为构建分布式、协同式的HarmonyOS应用生态奠定了坚实基础,是高级应用开发必备的核心技能。

需要参加鸿蒙认证的请点击 鸿蒙认证链接

posted @ 2025-10-30 10:57  ifeng918  阅读(15)  评论(0)    收藏  举报