通知扩展消息

重要

本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。

本章节介绍了如何使用阿里云移动推送平台推送鸿蒙通知扩展消息。

功能描述

鸿蒙推送平台提供了通知扩展消息能力,详情请参考发送通知扩展消息

鸿蒙平台通知扩展消息能力的重点说明

  • 通知扩展消息能力基于普通通知能力,需要先完成普通通知接入。

  • 通知扩展消息能力需要提前申请,参考申请推送通知扩展消息权益,没有申请时,即使是测试消息也无法发出。

  • 相对于普通通知能力,主要的变化如下:

    • 推送时,payload中新增extraData字段,传递额外数据,参考ExtensionPayload.extraData

    • 应用在后台时,通过RemoteNotificationExtensionAbility的onReceiveMessage方法接收推送,参考开发步骤的第3、4步。

    • 应用在前台时,通过pushService.receiveMessage方法注册的回调接口接收推送,参考开发步骤的第6步。

阿里云移动推送平台对通知扩展消息能力的支持

  • OpenAPI新增HarmonyExtensionPush和HarmonyExtensionExtraData字段以支持通知扩展消息推送。

  • 通过鸿蒙厂商通道推送时,阿里云移动推送平台对extraData字段进行了封装加密,并提供了API用于解析获取推送数据。

  • 推送时如果没有指定推送通道,而应用又在前台,则会通过阿里云自有通道推送通知扩展消息。

阿里云移动推送平台对通知扩展消息的支持,是基于鸿蒙平台的能力实现的,所以在实现时,先参考鸿蒙发送通知扩展消息文档做好准备工作和应用侧的代码实现。

推送通知扩展消息

OpenAPI新增字段实现通知扩展消息推送。在原来推送通知的基础上,增加HarmonyExtensionPush和HarmonyExtensionExtraData字段即可。

注意如果推送的消息,而非通知,设置HarmonyExtensionPush和HarmonyExtensionExtraData字段无效。

HarmonyExtensionPush和HarmonyExtensionExtraData字段的定义参考HarmonyExtensionPushHarmonyExtensionExtraData

接收通知扩展消息

通知扩展消息一共有三种接收方式:

  • 应用不在前台时,通过鸿蒙厂商通道下发,应用在RemoteNotificationExtensionAbility的onReceiveMessage方法中接收。

  • 应用在前台时,指定通过鸿蒙厂商通道下发,应用在pushService.receiveMessage方法注册的回调接口中接收。

  • 应用在前台时,默认通过阿里云自有通道下发,应用在SDK提供的IPushListener的onReceiveNotification方法中接收。

这三种情况是同时存在的,应用侧需要每一种都实现。

应用不在前台时,通过鸿蒙厂商通道下发

首先需要实现RemoteNotificationExtensionAbility,参考开发步骤的第3、4步。

阿里云移动推送平台对extraData字段进行了封装加密,SDK提供parseExtensionPushData方法解析推送数据。因此需要在onReceiveMessage方法中修改实现,在收到推送数据后,需要先解析,之后返回通知数据。完整的示例代码如下:

import { pushCommon, RemoteNotificationExtensionAbility } from '@kit.PushKit';
import { aliyunPush, ExtensionNotification } from '@aliyun/push';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';

export default class SampleRemoteNotification extends RemoteNotificationExtensionAbility {
  async onReceiveMessage(remoteNotificationInfo: pushCommon.RemoteNotificationInfo):
    Promise<pushCommon.RemoteNotificationContent> {
    // ************* 通知扩展消息处理 begin *************
    // 初始化推送参数,用于解析推送数据
    aliyunPush.init({
      appKey: '请填入在准备工作中查询的应用AppKey',
      appSecret: '请填入在准备工作中查询的应用AppSecret',
      context: this.context as common.Context,
    })

    // 调用parseExtensionPushData解析推送的参数
    const notification: ExtensionNotification = await aliyunPush.parseExtensionPushData(remoteNotificationInfo);

    // 这里可以获取通知扩展消息的参数进行业务处理

    // SDK提供辅助方法getRemoteNotificationContent构建返回值, 传参EntryAbility是本次通知点击要打开的Ability名称
    const result = aliyunPush.getPushHelper().getRemoteNotificationContent('EntryAbility', notification);
    return result;
    // 如果要修改通知的内容,可以返回想要修改的内容,wantAgent建议还是保留。如果不保留,后续就无法在用户点击时识别推送的参数,会丢失通知的点击数据
    // return {
    //   // 省略其它修改的字段,wantAgent建议还是使用getRemoteNotificationContent方法构造的参数。
    //   wantAgent: result.wantAgent
    // }
    // ************* 通知扩展消息处理 end *************
  }
}

大致分为三个部分:

  1. 调用aliyunPush.init配置推送参数。这里是因为RemoteNotificationExtensionAbility是独立运行的,没有正常的推送初始化代码,所以此处需要先配置参数,方便后续解析数据。

  2. 调用aliyunPush.parseExtensionPushData方法解析推送数据,获得ExtensionNotification,进行业务处理。

  3. 根据鸿蒙的要求,调用aliyunPush.getPushHelper().getRemoteNotificationContent方法构造返回的通知数据用于展示。这里参数'EntryAbility'需要替换为用户点击通知时要打开的Ability名字。

警告

注意:如果第三步没有通过aliyunPush.getPushHelper().getRemoteNotificationContent方法构造返回的通知数据,展示的通知内会丢失推送数据,当用户点击打开时,会被认为是普通的通知,不是推送产生的通知,无法拿到推送数据,参考从通知中获取推送数据

说明

应用不在前台,通过鸿蒙厂商通道下发通知扩展消息时,onReceiveMessage方法返回后,鸿蒙系统会展示通知,如果通知有角标参数,系统会自动处理,不需要单独处理。

ExtensionNotification

ExtensionNotification继承自PushNotification,定义如下:

export enum PushDataType {
  Notification = 1,
  Message = 2,
  ExtensionNotification = 3,
}

/**
 * 推送通知、消息公有的数据
 */
export interface BasePushData {
  // 省略其它参数
  /**
   * 数据类型, 1 推送通知 2 推送消息 3 通知扩展消息
   */
  type: PushDataType;
}
/**
 * 推送数据
 */
export interface PushNotification extends BasePushData {
  // 省略其它参数
}
  
export interface ExtensionNotification extends PushNotification {
  /**
   * 通知扩展消息的extraData字段
   */
  extensionExtraData?: string;
}

其中type字段为3时表示为通知扩展消息的推送数据,extensionExtraData表示服务设置的HarmonyExtensionExtraData字段。

应用在前台时,指定通过鸿蒙厂商通道下发

首先实现pushService.receiveMessage方法注册回调接口,参考开发步骤的第6步。

然后在回调接口中,调用aliyunPush.parseExtensionPushData方法解析推送数据,进行业务处理,示例代码如下:

import { UIAbility } from '@kit.AbilityKit';
import { pushService } from '@kit.PushKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { ExtensionNotification } from '@aliyun/push';
import { aliyunPush } from '@aliyun/push';
import { notificationManager } from '@kit.NotificationKit';

/**
 * 此处以EntryAbility为例,接收通知扩展消息内容
 */
export default class EntryAbility extends UIAbility {
  onCreate(): void {
    // ************* 初始化SDK begin *************
    aliyunPush.init({
      appKey: '请填入在准备工作中查询的应用AppKey',
      appSecret: '请填入在准备工作中查询的应用AppSecret',
      context: this.context,
    })
    // ************* 初始化SDK end *************

    // 省略其它初始化配置方法


    try {
      // receiveMessage中的参数固定为IM
      pushService.receiveMessage('IM', this, async (data) => {
        // process message,并建议对Callback进行try-catch
        try {
          // ************* 解析推送参数 begin *************
          const extensionNotification: ExtensionNotification = await aliyunPush.parseExtensionPushData(data);
          // ************* 解析推送参数 end *************
          // 这里根据获取的通知扩展消息参数进行业务处理
          hilog.info(0x0000, 'testTag', `Receive im message ${JSON.stringify(extensionNotification)}`);

          // 如果要展示通知,可以通过createNotificationRequest方法创建notificationManager.NotificationRequest进行展示
          const request = await aliyunPush.getPushHelper().createNotificationRequest(extensionNotification);
          await notificationManager.publish(request);

          // 如果通知扩展消息还携带了角标设置
          if(extensionNotification.badgeSetNum !== undefined && extensionNotification.badgeSetNum >= 0) {
            await notificationManager.setBadgeNumber(extensionNotification.badgeSetNum)
          }

        } catch (e) {
          let errRes: BusinessError = e as BusinessError;
          hilog.error(0x0000, 'testTag', 'Failed to process data: %{public}d %{public}s', errRes.code, errRes.message);
        }
      });
    } catch (err) {
      let e: BusinessError = err as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to get message: %{public}d %{public}s', e.code, e.message);
    }
  }
}

应用获取ExtensionNotification之后,正常进行业务处理即可。

如果还需要展示通知,可以参考上面的示例发布通知。

如果通知扩展消息还包括角标参数,可以参考上面的示例设置角标,逻辑与定制通知时的角标处理一致。

应用在前台时,通过阿里云自有通道下发

通过自有通道下发时,我们复用IPushListener的onReceiveNotification方法接收推送。整体逻辑参考接收推送

不同的地方是相关的参数扩展为接收ExtensionNotification类型。示例如下:

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { aliyunPush, ExtensionNotification, IPushListener,
  PushDataType,
  PushMessage, PushNotification } from '@aliyun/push';

// ************* IPushListener接口实现 begin *************
/**
 * 推送回调接口实现,用于接收推送数据
 */
class MyPushListener implements IPushListener {

  onReceiveNotification(data: PushNotification | ExtensionNotification): boolean {
    if (data.type === PushDataType.Notification) {
      // 处理推送通知
    } else if (data.type === PushDataType.ExtensionNotification) {
      // 处理通知扩展消息
    }
    // 返回false 表示不定制通知。返回true,表示定制通知。
    return false;
  }

  onShowNotification(data: PushNotification | ExtensionNotification): void {
    if (data.type === PushDataType.Notification) {
      // 处理通知展示事件
    } else if (data.type === PushDataType.ExtensionNotification) {
      // 处理通知扩展消息展示事件
    }
  }

  onReceiveMessage(data: PushMessage): void {
    // 处理推送消息
  }
}
// ************* IPushListener接口实现 end *************

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {

    aliyunPush.init({
      appKey: '请填入在SDK接入准备工作中查询的应用AppKey',
      appSecret: '请填入在SDK接入准备工作中查询的应用AppSecret',
      context: this.context,
    })

    // ************* 注册IPushListener接口 begin *************
    aliyunPush.setPushListener(new MyPushListener());
    // ************* 注册IPushListener接口 end *************
  }

  // 省略其它代码

  onDestroy(): void {
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        return;
      }
    });
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
  }

  onForeground(): void {
    // Ability has brought to foreground
  }

  onBackground(): void {
    // Ability has back to background
  }
}