第49篇:鸿蒙推送服务——PushUtils 与通知管理

img

一、引言

推送服务是应用主动触达用户的重要渠道,用于发送考试提醒、学习通知、模考成绩等消息。DriverLicenseExam 项目通过鸿蒙推送服务实现了消息推送功能,包括通知权限申请、消息构建、推送发送、点击处理等完整流程。本文将深入解析推送服务的实现。

二、推送服务架构

2.1 推送流程

应用启动
    │
    ▼
申请通知权限(requestEnableNotification)
    │
    ▼
权限已授权?
    ├── 否 → 不发送推送
    │
    └── 是 → 构建推送消息
        │
        ▼
        PushUtils.randomPushMessage()
        │
        ▼
        系统通知栏显示
        │
        ▼
        用户点击通知
        │
        ▼
        EntryAbility.onNewWant()
        │
        ▼
        解析参数 → 跳转到指定页面

2.2 推送相关文件

commons/commonLib/src/main/ets/push/
  ├── Model.ets       ← 推送数据模型
  └── PushUtils.ets   ← 推送工具类

三、推送权限管理

3.1 通知权限申请

在 EntryAbility 的 onWindowStageCreate 中申请通知权限:

// EntryAbility.ets
notificationManager.requestEnableNotification(this.context).then(() => {
  hilog.info(0x0000, 'testTag', '[ANS] requestEnableNotification success');
}).catch((err: BusinessError) => {
  hilog.error(0x0000, 'testTag',
    '[ANS] requestEnableNotification failed, code: ' + err.code + ', message: ' + err.message);
});

3.2 权限检查

在发送推送前检查通知权限是否已开启:

// MainEntry.ets
sendPushNotice() {
  const isOn = notificationManager.isNotificationEnabledSync();
  if (isOn) {
    this.sendPushMessage();
  }
}

isNotificationEnabledSync() 是同步方法,快速检查通知权限状态,避免在未授权时进行不必要的推送操作。

四、推送消息模型

4.1 推送参数定义

// PushUtils.ets
export interface PushActionParams {
  picVideoUrl: string;   // 图片/视频 URL(用于富媒体通知)
  title: string;         // 推送标题
  id: string;            // 推送 ID(用于去重和追踪)
}

4.2 推送消息构建

// PushUtils.ets
export class PushUtils {
  // 构建推送通知请求
  static buildNotificationRequest(params: PushActionParams): notificationManager.NotificationRequest {
    let notificationRequest: notificationManager.NotificationRequest = {
      id: parseInt(params.id) || 0,
      content: {
        contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: params.title,
          text: '点击查看详情',
          additionalText: params.picVideoUrl || '',
        },
      },
      // 点击通知后携带的参数
      wantAgent: {
        // 通过 Want 传递参数到 Ability
        want: {
          deviceId: '',
          bundleName: 'qcjk.1.xxxxxx',
          abilityName: 'EntryAbility',
          parameters: {
            params: JSON.stringify({
              message: 'practiceView',
              title: params.title,
              id: params.id,
            }),
          },
        },
      },
    };
    return notificationRequest;
  }
}

五、推送消息发送

5.1 随机推送消息

// PushUtils.ets
static randomPushMessage(params: PushActionParams, context: Context) {
  // 随机决定是否发送推送
  if (Math.random() > 0.3) {  // 30% 概率发送
    return;
  }

  const notificationRequest = this.buildNotificationRequest(params);
  
  notificationManager.publish(notificationRequest)
    .then(() => {
      Logger.info('PushUtils', 'Push notification published successfully');
    })
    .catch((err: BusinessError) => {
      Logger.error('PushUtils', 'Failed to publish notification: ' + err.message);
    });
}

5.2 在主页面触发推送

// MainEntry.ets
aboutToAppear(): void {
  this.bottomRectHeight = AppStorage.get('bottomRectHeight') || 0;
  this.vm.navStack.pushPathByName('splashPage', true);
  this.sendPushNotice();  // 启动时尝试发送推送
  this.updateForm(0);
}

sendPushMessage() {
  let pushArticle: PushActionParams = {
    picVideoUrl: '',
    title: '驾考模板',
    id: '3445749589458989',
  };
  PushUtils.randomPushMessage(pushArticle, this.getUIContext().getHostContext() as Context);
}

六、推送点击处理

6.1 Want 参数解析

当用户点击通知栏的推送消息时,系统会通过 Want 参数将消息传递到 Ability:

// EntryAbility.ets - 处理推送点击
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  this.setLightOrDarkMode(this.context);
  Logger.info(TAG, 'Ability onNewWant');

  if (want.parameters && want.parameters.params) {
    let param: ESObject = {};
    try {
      param = JSON.parse(want.parameters.params as string);
    } catch (e) {
      Logger.error(TAG, 'Failed to parse push params: ' + JSON.stringify(e));
    }

    // 根据推送消息类型跳转到不同页面
    if (param.message === 'practiceView') {
      // 跳转到模拟考试
      const examService = ExamService.instance(this.context as Context);
      const par: ROUTE_PARAM = {
        title: '模拟考试',
        type: EXAM_MANAGER_TYPE.mock_exam,
        examManager: examService.getMockExamManager('模拟考试'),
      };
      CommonModel.instance.navStack.replacePathByName('practiceView', par);
    } else if (param.message === 'orderPractice') {
      // 跳转到顺序练习
      const param: ROUTE_PARAM = {
        title: '顺序练习',
        type: EXAM_MANAGER_TYPE.sequence,
      };
      CommonModel.instance.navStack.replacePathByName('practiceView', param);
    }
  }

  WantUtils.handlePushWant(want);
  this.shareServiceImpl.handleWant(want, this.context);
}

6.2 冷启动与热启动处理

推送点击有两种场景:

  1. 冷启动:应用未运行,点击通知启动应用 → 在 onCreate 中处理
  2. 热启动:应用已在后台,点击通知唤醒应用 → 在 onNewWant 中处理
// 冷启动处理
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
  Logger.info(TAG, 'Ability onCreate');
  WantUtils.handlePushCall(want);  // 处理冷启动推送
  this.shareServiceImpl.handleWant(want, this.context);
}

// 热启动处理
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  // 处理热启动推送
  WantUtils.handlePushWant(want);
  this.shareServiceImpl.handleWant(want, this.context);
}

七、推送功能的扩展

7.1 本地通知

除了服务器推送,应用还可以发送本地通知,例如模拟考试完成后的成绩通知:

static sendLocalNotification(title: string, content: string, context: Context) {
  const notificationRequest: notificationManager.NotificationRequest = {
    id: Date.now(),
    content: {
      contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
      normal: {
        title: title,
        text: content,
      },
    },
  };

  notificationManager.publish(notificationRequest)
    .then(() => Logger.info('PushUtils', 'Local notification published'))
    .catch((err) => Logger.error('PushUtils', 'Failed to publish: ' + err.message));
}

7.2 推送分类

不同类型的推送可以使用不同的通知渠道:

// 通知渠道分类
const NOTIFICATION_CHANNELS = {
  EXAM_REMINDER: { id: 'exam_reminder', name: '考试提醒', importance: 4 },
  STUDY_TIP: { id: 'study_tip', name: '学习建议', importance: 3 },
  PROMOTION: { id: 'promotion', name: '活动推广', importance: 2 },
};

八、总结

推送服务是应用与用户保持互动的重要渠道。DriverLicenseExam 项目通过完整的推送实现展示了:

  1. 权限管理:通知权限的申请和检查
  2. 消息构建:NotificationRequest 的消息结构
  3. 推送发送:通过 notificationManager.publish 发送
  4. 点击处理:通过 Want 参数传递,支持冷启动和热启动
  5. 页面跳转:根据推送类型跳转到不同页面

关键源码文件:

  • commons/commonLib/src/main/ets/push/PushUtils.ets — 推送工具类
  • commons/commonLib/src/main/ets/push/Model.ets — 推送数据模型
  • products/entry/src/main/ets/entryability/EntryAbility.ets — 推送点击处理
  • products/entry/src/main/ets/pages/MainEntry.ets — 推送触发入口
  • products/entry/src/main/ets/util/WantUtils.ets — Want 参数处理工具
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐