HarmonyOS事件订阅与通知:后台事件处理

本文将深入探讨HarmonyOS 5(API 12)中的事件订阅与通知机制,重点讲解如何在后台处理事件,实现应用的实时响应和跨设备协同。内容涵盖核心API、实现步骤、实战示例及性能优化建议。

1. 事件订阅与通知机制概述

HarmonyOS的事件系统采用发布-订阅模式,允许应用在后台监听和处理系统事件、自定义事件及跨设备事件。其核心优势包括:

  • 跨进程通信:支持应用间事件传递,解耦组件依赖。
  • 后台唤醒:即使应用处于后台,也能通过事件唤醒并执行任务。
  • 统一事件管理:提供通用的事件订阅、发布和取消订阅接口。
  • 跨设备同步:结合分布式能力,实现事件在多个设备间同步触发。

1.1 核心API介绍

HarmonyOS 5的事件处理主要依赖以下模块:

  • @ohos.commonEventManager:系统通用事件管理,如屏幕亮灭、网络变化等。
  • @ohos.remoteNotification:远程通知管理,用于处理推送事件。
  • @ohos.distributedDeviceManager:分布式设备管理,支持跨设备事件订阅。
  • BackgroundTaskManager:后台任务管理,确保事件处理在后台可持续运行。

2. 通用事件订阅与处理

通用事件(CommonEvent)是系统预定义的事件(如时间变化、网络状态变化),应用可订阅这些事件并在后台响应。

2.1 订阅系统事件

以下示例演示如何订阅屏幕亮灭事件,并在后台处理:

import commonEventManager from '@ohos.commonEventManager';
import commonEvent from '@ohos.commonEvent';
import { BusinessError } from '@ohos.base';

@Entry
@Component
struct CommonEventDemo {
  // 订阅者对象
  private subscriber: commonEventManager.CommonEventSubscriber | null = null;
  // 订阅参数
  private subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
    events: [commonEvent.SCREEN_OFF, commonEvent.SCREEN_ON] // 订阅事件列表
  };

  // 组件初始化时订阅事件
  async aboutToAppear() {
    await this.subscribeCommonEvent();
  }

  // 订阅事件
  private async subscribeCommonEvent() {
    try {
      // 创建订阅者
      this.subscriber = await commonEventManager.createSubscriber(this.subscribeInfo);
      
      // 注册事件回调
      this.subscriber.on('receive', (event: commonEventManager.CommonEventData) => {
        console.log(`收到事件: ${event.event}`);
        this.handleScreenEvent(event.event);
      });
      
      console.log('通用事件订阅成功');
    } catch (error) {
      console.error(`订阅失败: ${(error as BusinessError).message}`);
    }
  }

  // 处理屏幕事件
  private handleScreenEvent(event: string) {
    // 后台处理逻辑:即使应用在后台也能执行
    switch (event) {
      case commonEvent.SCREEN_ON:
        // 屏幕点亮时处理
        this.onScreenOn();
        break;
      case commonEvent.SCREEN_OFF:
        // 屏幕熄灭时处理
        this.onScreenOff();
        break;
    }
  }

  private onScreenOn() {
    // 示例:屏幕亮起时刷新数据
    console.log('屏幕已点亮,开始后台数据同步...');
    this.refreshData();
  }

  private onScreenOff() {
    // 示例:屏幕熄灭时释放资源
    console.log('屏幕已熄灭,释放非必要资源...');
    this.releaseResources();
  }

  // 后台数据刷新
  private async refreshData() {
    // 实际项目中可从这里发起网络请求或更新本地数据
    console.log('执行后台数据刷新任务');
  }

  // 资源释放
  private releaseResources() {
    console.log('释放音频、传感器等资源');
  }

  // 取消订阅
  private async unsubscribeCommonEvent() {
    if (this.subscriber) {
      try {
        await commonEventManager.unsubscribe(this.subscriber);
        console.log('取消订阅成功');
      } catch (error) {
        console.error(`取消订阅失败: ${(error as BusinessError).message}`);
      }
    }
  }

  // 组件销毁时取消订阅
  aboutToDisappear() {
    this.unsubscribeCommonEvent();
  }

  build() {
    Column() {
      Text('通用事件订阅示例')
        .fontSize(20)
        .margin(10)
      
      Text('尝试开关屏幕查看控制台输出')
        .fontSize(16)
        .margin(10)
    }
    .width('100%')
    .height('100%')
  }
}

2.2 支持订阅的常见系统事件

事件类型 常量名 触发条件 后台处理能力
屏幕点亮 SCREEN_ON 屏幕从熄灭变亮 支持
屏幕熄灭 SCREEN_OFF 屏幕熄灭 支持
充电状态 BATTERY_CHARGING 开始充电 支持
充电完成 BATTERY_FULL 电池充满 支持
网络变化 NETWORK_AVAILABLE 网络连接状态变化 支持
时间变化 TIME_TICK 系统时间每分钟变化 支持
包安装 PACKAGE_ADDED 新应用安装 支持
包移除 PACKAGE_REMOVED 应用被卸载 支持

3. 自定义事件与跨设备事件

除了系统事件,应用还可以定义和发布自己的事件,并支持跨设备同步。

3.1 自定义事件发布与订阅

import commonEventManager from '@ohos.commonEventManager';
import commonEvent from '@ohos.commonEvent';
import { BusinessError } from '@ohos.base';

// 定义自定义事件
const CUSTOM_EVENTS = {
  USER_LOGIN: 'com.example.APP.USER_LOGIN',
  DATA_UPDATED: 'com.example.APP.DATA_UPDATED'
};

@Entry
@Component
struct CustomEventDemo {
  private subscriber: commonEventManager.CommonEventSubscriber | null = null;
  
  // 订阅自定义事件
  private async subscribeCustomEvents() {
    const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
      events: [CUSTOM_EVENTS.USER_LOGIN, CUSTOM_EVENTS.DATA_UPDATED]
    };
    
    try {
      this.subscriber = await commonEventManager.createSubscriber(subscribeInfo);
      
      this.subscriber.on('receive', (event: commonEventManager.CommonEventData]) => {
        console.log(`收到自定义事件: ${event.event}`);
        console.log(`事件数据: ${JSON.stringify(event.parameters)}`);
        
        // 根据事件类型处理
        switch (event.event) {
          case CUSTOM_EVENTS.USER_LOGIN:
            this.handleUserLogin(event.parameters);
            break;
          case CUSTOM_EVENTS.DATA_UPDATED:
            this.handleDataUpdated(event.parameters);
            break;
        }
      });
      
      console.log('自定义事件订阅成功');
    } catch (error) {
      console.error(`自定义事件订阅失败: ${(error as BusinessError).message}`);
    }
  }
  
  // 发布自定义事件
  private async publishCustomEvent(event: string, params: Record<string, Object>) {
    try {
      await commonEventManager.publish(event, {
        parameters: params
      });
      console.log(`事件发布成功: ${event}`);
    } catch (error) {
      console.error(`事件发布失败: ${(error as BusinessError).message}`);
    }
  }
  
  // 处理用户登录事件
  private handleUserLogin(params: Record<string, Object>) {
    const username = params['username'] || '未知用户';
    console.log(`用户登录: ${username}`);
    // 执行后台任务,如同步用户数据
  }
  
  // 处理数据更新事件
  private handleDataUpdated(params: Record<string, Object>) {
    const dataType = params['dataType'] || '未知类型';
    console.log(`数据已更新: ${dataType}`);
    // 执行后台任务,如刷新缓存
  }
  
  build() {
    Column() {
      Button('发布用户登录事件')
        .onClick(() => {
          this.publishCustomEvent(CUSTOM_EVENTS.USER_LOGIN, {
            username: '张三',
            userId: '12345',
            timestamp: Date.now()
          });
        })
        .margin(10)
        .width('80%')
      
      Button('发布数据更新事件')
        .onClick(() => {
          this.publishCustomEvent(CUSTOM_EVENTS.DATA_UPDATED, {
            dataType: '用户配置',
            updateCount: 5,
            timestamp: Date.now()
          });
        })
        .margin(10)
        .width('80%')
    }
    .width('100%')
    .height('100%')
    .onAppear(() => {
      this.subscribeCustomEvents();
    })
    .onDisappear(() => {
      if (this.subscriber) {
        commonEventManager.unsubscribe(this.subscriber);
      }
    })
  }
}

3.2 跨设备事件同步

HarmonyOS的分布式能力允许事件在多个设备间同步触发。

import deviceManager from '@ohos.distributedDeviceManager';
import commonEventManager from '@ohos.commonEventManager';
import { BusinessError } from '@ohos.base';

@Entry
@Component
struct DistributedEventDemo {
  // 获取可信设备列表
  private getTrustedDevices(): string[] {
    try {
      const devices = deviceManager.getTrustedDeviceListSync();
      return devices.map(device => device.deviceId);
    } catch (error) {
      console.error(`获取设备列表失败: ${(error as BusinessError).message}`);
      return [];
    }
  }
  
  // 发布跨设备事件
  private async publishDistributedEvent(event: string, params: Record<string, Object>) {
    try {
      const devices = this.getTrustedDevices();
      
      if (devices.length > 0) {
        // 向所有可信设备发布事件
        await commonEventManager.publish(event, {
          parameters: params,
          devices: devices // 指定目标设备
        });
        console.log(`跨设备事件发布成功,目标设备: ${devices.length}个`);
      } else {
        console.log('无可信设备,仅在本地发布');
        await commonEventManager.publish(event, { parameters: params });
      }
    } catch (error) {
      console.error(`跨设备事件发布失败: ${(error as BusinessError).message}`);
    }
  }
  
  // 订阅跨设备事件
  private async subscribeDistributedEvent() {
    const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
      events: ['com.example.DISTRIBUTED_ACTION'],
      isDistributed: true // 关键:启用分布式订阅
    };
    
    try {
      const subscriber = await commonEventManager.createSubscriber(subscribeInfo);
      
      subscriber.on('receive', (event: commonEventManager.CommonEventData) => {
        console.log(`收到跨设备事件: ${event.event}`);
        console.log(`来源设备: ${event.deviceId}`);
        console.log(`事件数据: ${JSON.stringify(event.parameters)}`);
        
        // 处理跨设备事件
        this.handleDistributedEvent(event);
      });
      
      console.log('跨设备事件订阅成功');
    } catch (error) {
      console.error(`跨设备事件订阅失败: ${(error as BusinessError).message}`);
    }
  }
  
  private handleDistributedEvent(event: commonEventManager.CommonEventData) {
    // 实现跨设备事件处理逻辑
    console.log(`处理来自设备 ${event.deviceId} 的事件`);
  }
}

4. 后台持续任务处理

为确保事件处理在后台持续运行,需要使用BackgroundTaskManager申请后台权限。

4.1 后台任务配置与申请

首先,在module.json5中声明后台权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
        "reason": "$string:background_permission_reason",
        "usedScene": {
          "abilities": ["MainAbility"],
          "when": "always"
        }
      }
    ]
  }
}

然后在代码中申请后台任务权限:

import backgroundTaskManager from '@ohos.resources.backgroundTaskManager';
import { BusinessError } from '@ohos.base';

@Entry
@Component
struct BackgroundEventDemo {
  private backgroundTaskId: number = 0;
  
  // 申请后台任务权限
  private async requestBackgroundPermission() {
    try {
      const result = await backgroundTaskManager.requestSuspendDelay();
      console.log('后台任务权限申请成功');
      
      // 注册后台任务回调
      this.backgroundTaskId = await backgroundTaskManager.startBackgroundRunning(
        backgroundTaskManager.BackgroundMode.DATA_PROCESSING,
        {
          // 任务配置
          isPersistent: true, // 持久化任务
          notificationContent: {
            title: '后台事件处理中',
            text: '应用正在后台处理事件'
          }
        }
      );
      
      console.log(`后台任务启动成功,ID: ${this.backgroundTaskId}`);
    } catch (error) {
      console.error(`后台任务申请失败: ${(error as BusinessError).message}`);
    }
  }
  
  // 停止后台任务
  private async stopBackgroundTask() {
    if (this.backgroundTaskId !== 0) {
      try {
        await backgroundTaskManager.stopBackgroundRunning(this.backgroundTaskId);
        console.log('后台任务已停止');
      } catch (error) {
        console.error(`停止后台任务失败: ${(error as BusinessError).message}`);
      }
    }
  }
  
  build() {
    Column() {
      Button('启动后台事件监听')
        .onClick(() => {
          this.requestBackgroundPermission();
        })
        .margin(10)
        .width('80%')
      
      Button('停止后台监听')
        .onClick(() => {
          this.stopBackgroundTask();
        })
        .margin(10)
        .width('80%')
    }
    .width('100%')
    .height('100%')
  }
}

5. 实战案例:后台数据同步器

以下是一个完整的后台数据同步器示例,结合事件订阅和后台任务:

import commonEventManager from '@ohos.commonEventManager';
import commonEvent from '@ohos.commonEvent';
import backgroundTaskManager from '@ohos.resources.backgroundTaskManager';
import { BusinessError } from '@ohos.base';

@Entry
@Component
struct BackgroundDataSync {
  private subscriber: commonEventManager.CommonEventSubscriber | null = null;
  private backgroundTaskId: number = 0;
  @State syncStatus: string = '未启动';
  
  // 订阅网络变化事件
  private async setupNetworkListener() {
    const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
      events: [commonEvent.NETWORK_AVAILABLE]
    };
    
    try {
      this.subscriber = await commonEventManager.createSubscriber(subscribeInfo);
      
      this.subscriber.on('receive', (event: commonEventManager.CommonEventData) => {
        console.log('网络状态变化,触发后台同步');
        this.syncDataInBackground();
      });
      
      console.log('网络监听器设置成功');
    } catch (error) {
      console.error(`网络监听设置失败: ${(error as BusinessError).message}`);
    }
  }
  
  // 后台数据同步
  private async syncDataInBackground() {
    this.syncStatus = '同步中...';
    
    try {
      // 模拟网络请求
      console.log('开始在后台同步数据');
      
      // 实际项目中这里可能是API调用或数据库操作
      await new Promise(resolve => setTimeout(resolve, 2000));
      
      console.log('后台数据同步完成');
      this.syncStatus = `最后同步: ${new Date().toLocaleTimeString()}`;
      
      // 发布同步完成事件
      await commonEventManager.publish('com.example.DATA_SYNC_COMPLETE', {
        result: 'success',
        timestamp: Date.now()
      });
      
    } catch (error) {
      console.error(`数据同步失败: ${(error as BusinessError).message}`);
      this.syncStatus = '同步失败';
    }
  }
  
  // 启动后台同步服务
  private async startSyncService() {
    // 申请后台权限
    try {
      this.backgroundTaskId = await backgroundTaskManager.startBackgroundRunning(
        backgroundTaskManager.BackgroundMode.DATA_PROCESSING,
        {
          isPersistent: true,
          notificationContent: {
            title: '数据同步服务运行中',
            text: '应用正在后台保持数据同步'
          }
        }
      );
      
      await this.setupNetworkListener();
      this.syncStatus = '监听中,等待网络变化...';
      console.log('后台同步服务启动成功');
      
    } catch (error) {
      console.error(`后台服务启动失败: ${(error as BusinessError).message}`);
    }
  }
  
  // 停止服务
  private async stopSyncService() {
    if (this.subscriber) {
      await commonEventManager.unsubscribe(this.subscriber);
      this.subscriber = null;
    }
    
    if (this.backgroundTaskId !== 0) {
      await backgroundTaskManager.stopBackgroundRunning(this.backgroundTaskId);
      this.backgroundTaskId = 0;
    }
    
    this.syncStatus = '已停止';
    console.log('同步服务已停止');
  }
  
  aboutToDisappear() {
    this.stopSyncService();
  }
  
  build() {
    Column() {
      Text('后台数据同步器')
        .fontSize(20)
        .margin(10)
      
      Text(`状态: ${this.syncStatus}`)
        .fontSize(16)
        .margin(10)
      
      Button('启动后台同步')
        .onClick(() => {
          this.startSyncService();
        })
        .margin(10)
        .width('80%')
      
      Button('手动同步')
        .onClick(() => {
          this.syncDataInBackground();
        })
        .margin(10)
        .width('80%')
      
      Button('停止服务')
        .onClick(() => {
          this.stopSyncService();
        })
        .margin(10)
        .width('80%')
    }
    .width('100%')
    .height('100%')
  }
}

6. 性能优化与最佳实践

  1. 事件过滤:精确订阅需要的事件类型,避免不必要的通知。

    // 好:精确订阅特定事件
    const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
      events: [commonEvent.SCREEN_ON, commonEvent.SCREEN_OFF]
    };
    
    // 不好:订阅过多不必要的事件
    const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
      events: [commonEvent.*] // 避免这样使用
    };
    
  2. 后台任务精简:后台处理逻辑应轻量高效,避免耗电和资源占用。

    // 好:高效的后台处理
    private async handleBackgroundEvent() {
      // 只执行必要的轻量操作
      await this.updateEssentialData();
    }
    
    // 不好:在后台执行重操作
    private async handleBackgroundEvent() {
      // 避免在后台执行大量计算或频繁网络请求
      await this.downloadLargeFiles(); // 不适合在后台执行
    }
    
  3. 及时取消订阅:在组件销毁或页面消失时取消事件订阅。

    aboutToDisappear() {
      if (this.subscriber) {
        commonEventManager.unsubscribe(this.subscriber);
      }
    }
    
  4. 分布式事件优化:跨设备事件应压缩数据量,减少网络传输。

    // 好:只发送必要数据
    await commonEventManager.publish('user_update', {
      parameters: {
        userId: '123',
        // 只发送变更字段
        updatedFields: ['name', 'avatar']
      }
    });
    
  5. 错误处理与重试:实现健壮的错误处理和重试机制。

    private async safePublishEvent(event: string, params: Record<string, Object>, retries = 3) {
      for (let i = 0; i < retries; i++) {
        try {
          await commonEventManager.publish(event, { parameters: params });
          return; // 成功则退出
        } catch (error) {
          if (i === retries - 1) {
            throw error; // 最后一次失败后抛出
          }
          await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒后重试
        }
      }
    }
    

7. 总结

HarmonyOS 5的事件订阅与通知机制为后台事件处理提供了强大支持,关键要点包括:

  • 系统事件订阅:通过commonEventManager订阅系统事件,实现后台响应。
  • 自定义事件:支持应用定义和发布自己的事件,实现组件间解耦。
  • 跨设备同步:利用分布式能力实现事件在多设备间同步触发。
  • 后台持久化:使用BackgroundTaskManager确保事件处理在后台持续运行。
  • 性能优化:通过事件过滤、精简任务和及时取消订阅提升性能。

通过合理利用这些能力,开发者可以构建出响应迅速、跨设备协同的后台服务,提升用户体验和应用价值。

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

posted @ 2025-09-24 16:27  猫林老师  阅读(36)  评论(0)    收藏  举报