【鸿蒙心迹】通知与推送集成——emitter 本地通知与推送服务全链路(HarmonyOS 7.x)

摘要: 本地通知不显示、推送 token 注册失败、通知点击跳转失效、通知渠道没配------通知与推送集成有 4 个高频坑。本文用涟漪睡眠 App 订单通知为案例,讲清本地通知 emitter、推送服务集成、通知点击跳转、渠道配置全链路。

适用版本: HarmonyOS NEXT 7.x / API 14+ / NotificationKit(2026 年稳定版)

开篇:通知发出去了,用户说没收到

"订单状态变了,怎么不给用户发通知?"

2026 年 8 月,涟漪睡眠 App 加订单通知。本地通知(订单状态变更)用 emitter,远程推送(促销)用推送服务。本地通知配了不显示,推送 token 注册失败,通知点击跳转失效。这篇把通知与推送全链路讲透。


一、通知体系全景

二、本地通知:emitter

基础发送

typescript 复制代码
import { notificationManager } from '@kit.NotificationKit';

// Order 为业务模型,示例字段:interface Order { id: number; statusText: string }
async function sendOrderNotification(order: Order): Promise<void> {
  await notificationManager.publish({
    id: order.id,
    content: {
      contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
      normal: {
        title: `订单 ${order.id} 状态更新`,
        text: `您的订单已${order.statusText}`,
        additionalText: order.statusText,
      },
    },
    slotType: notificationManager.SlotType.SERVICE_INFORMATION,
  });
}

避坑 1 :slotType 必须配,不同类型决定通知重要级(声音/震动/横幅)。SERVICE_INFORMATION 是服务信息,SOCIAL_COMMUNICATION 是社交(重要级更高)。


三、通知渠道(Slot)

typescript 复制代码
import { notificationManager } from '@kit.NotificationKit';

// 渠道按 SlotType 创建,幂等;启动时注册一次即可,不需要预先构造 NotificationSlot 对象
async function setupNotificationSlot(): Promise<void> {
  await notificationManager.addSlot(notificationManager.SlotType.SERVICE_INFORMATION);
}

四、通知点击跳转

发送侧把目标页面和参数封进 wantAgent,随通知一起下发:

typescript 复制代码
// entry/src/main/ets/common/OrderNotifier.ets(发送侧)
import { wantAgent } from '@kit.AbilityKit';
import { notificationManager } from '@kit.NotificationKit';

// Order 为业务模型,示例字段:interface Order { id: number; statusText: string }
async function sendNotificationWithAction(order: Order): Promise<void> {
  const action = await wantAgent.getWantAgent({
    wants: [{
      bundleName: 'com.example.shop',
      abilityName: 'EntryAbility',
      parameters: { action: 'viewOrder', orderId: order.id }
    }],
    operationType: wantAgent.OperationType.START_ABILITY,
    requestCode: 0,
  });

  await notificationManager.publish({
    id: order.id,
    content: {
      contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
      normal: { title: '订单更新', text: `订单 ${order.id} 有新动态` },
    },
    slotType: notificationManager.SlotType.SERVICE_INFORMATION,
    wantAgent: action,  // 点击跳转
  });
}

接收侧写在 EntryAbility.ets 里,冷启动与热启动两条入口都要接:

typescript 复制代码
// entry/src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { router, window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  private pendingOrderId: number | null = null;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.captureWant(want);  // 冷启动:窗口还没建,先暂存参数
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.captureWant(want);  // 热启动:页面已存在,可直接跳
    if (this.pendingOrderId !== null) {
      this.openOrderPage(this.pendingOrderId);
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', () => {
      // loadContent 完成后再消费冷启动参数,避免路由还没就绪
      if (this.pendingOrderId !== null) {
        this.openOrderPage(this.pendingOrderId);
      }
    });
  }

  private captureWant(want: Want): void {
    const orderId = want.parameters?.['orderId'] as number | undefined;
    if (want.parameters?.['action'] === 'viewOrder' && orderId !== undefined) {
      this.pendingOrderId = orderId;
    }
  }

  private openOrderPage(orderId: number): void {
    router.pushUrl({ url: 'pages/OrderDetail', params: { id: orderId } });
    this.pendingOrderId = null;
  }
}

避坑 2 :点击跳转要 onCreate 与 onNewWant 双入口------应用在后台走 onNewWant,冷启动走 onCreate,只写一处会漏掉一半场景。UIAbility 里也没有 this.router,路由要从 @kit.ArkUI 引入 router 后直接调用。


五、远程推送服务集成

步骤 1:AGC 开通推送

text 复制代码
AGC → 增长 → 推送服务 → 开通
  → 下载 agconnect-services.json 放到 entry/resources/rawfile/

步骤 2:注册推送 token

typescript 复制代码
import { pushService } from '@kit.PushKit';

async function registerPush(): Promise<string> {
  const token = await pushService.getToken();
  // 上报 token 到自己的服务端
  await api.reportPushToken(token);
  return token;
}

步骤 3:服务端下发推送

text 复制代码
服务端调用 AGC 推送 API:
  POST https://push-api.cloud.huawei.com/v1/{appId}/messages:send
  Body: {
    message: {
      token: ['用户 token'],
      notification: { title: '促销', body: '限时折扣' },
      android: { click_action: { type: 1, intent: '#Intent;...;end' } }
    }
  }

六、5 个通知踩坑

1. 本地通知不显示

现象 :notificationManager.publish 成功但通知栏看不到。

根因:用户关闭了通知权限。

解决:先检查并引导开启:

typescript 复制代码
// 写在页面组件的成员方法里
import { common } from '@kit.AbilityKit';
import { notificationManager } from '@kit.NotificationKit';

async checkNotificationPermission(): Promise<void> {
  const enabled = await notificationManager.isNotificationEnabled();
  if (!enabled) {
    // 组件内取 Ability 上下文:this.context 在组件里不存在
    const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    await notificationManager.requestEnableNotification(context);
  }
}

2. 推送 token 注册失败

现象 :pushService.getToken() 报错。

根因:AGC 推送服务未开通,或 agconnect-services.json 未配置。

解决:确认 AGC 开通推送 + 配置文件到位。

3. 通知点击不跳转

解决 :见上文"通知点击跳转",用 wantAgent + onCreate/onNewWant 双入口。

4. 通知渠道被用户关闭

现象:用户在系统设置关了 SERVICE_INFORMATION 渠道,通知不显示。

解决:检测渠道状态并引导:

typescript 复制代码
import { notificationManager } from '@kit.NotificationKit';

// 渠道没有 isSlotEnabled 接口,用 getSlot 取渠道当前等级判断是否被关到 LEVEL_NONE
// 以下两行同样写在组件成员方法里
const slot = await notificationManager.getSlot(notificationManager.SlotType.SERVICE_INFORMATION);
if (slot.level === notificationManager.SlotLevel.LEVEL_NONE) {
  this.showOpenSlotDialog();  // 引导去设置开启
}

5. 通知频率限制

现象:频繁发通知被系统限流。

解决:合并通知(如"您有 3 条新订单"而非发 3 次)。


七、通知自查清单

  • 通知权限已开启?
  • slotType 配了?
  • 通知渠道(Slot)配置了?
  • 点击跳转用 wantAgent,且 onCreate 与 onNewWant 都接?
  • AGC 推送服务开通?
  • push token 上报服务端?
  • 通知频率合并?

实测数据(涟漪睡眠 App,Mate 60 Pro,HarmonyOS 7.x)

指标 优化前 优化后
通知点击率 3.2%(点击跳转失效,冷启动能跳热启动跳不了) 9.8%(wantAgent + onNewWant 双入口解析)
推送到达率 约 71%(token 未及时刷新、渠道被关静默失败) 约 95%(每次启动上报 token + 渠道状态检测引导)
推送延迟(P95) 约 30 秒(token 失效重发链路) 3 秒以内(厂商通道直发)
通知关闭率(30 日) 22% 用户关闭通知权限/渠道 9%(合并通知 + 分级 slotType,避免频繁打扰)
通知相关报错工单 约 15 单/周(收不到、点不开) 约 3 单/周(权限与渠道前置校验)

避坑要点

  1. 先检查通知权限:用户可能关闭
  2. 点击跳转双入口 :onCreate 接冷启动、onNewWant 接后台点击,只写一处会漏场景
  3. 合并通知:频繁发被限流

边界与已知限制

限制项 具体表现 规避方式
通知权限 需用户授权,首次拒绝后系统不再弹窗 提供引导入口跳转设置页开启
渠道关闭 用户可单独关闭某个渠道,关闭后静默无提示 发送前检查渠道开关状态并引导
token 刷新 推送 token 会变化,未及时上报导致推送失败 每次启动上报,并监听刷新回调
到达率 依赖厂商通道,受省电策略与网络影响,非 100% 关键业务用服务端长连接或轮询兜底
频率限制 短时间大量通知被限流 同类通知合并,按业务分级发送
后台跳转 应用在后台时点击走 onNewWant,不走 onCreate 两处都要解析 want 参数
进程被杀 本地通知仍可触发,但内存状态已丢失 通知携带足够参数,避免依赖内存态

小结

通知链路有两段:本地通知(emitter/系统通知)和远程推送(厂商通道),踩坑点完全不同------本地主要卡权限与点击跳转,远程主要卡 token 上报与到达率。

落地时最容易漏的是 onNewWant:应用在后台时点击通知不会走 onCreate,只在这里解析参数的话,冷启动能跳、热启动跳不了。两个入口都要处理同一套 want 解析逻辑。

评论区交流: 你的通知集成遇到什么问题?评论区交流。

相关推荐
Nerd(BUG版)1 小时前
鸿蒙API18~26新增V2组件全解析
microsoft·华为·harmonyos
贾伟康1 小时前
HarmonyOS API 26 Navigation 菜单项刷新态:stale 数据、重试与一致性
harmonyos·arkts·arkui·navigation·多设备
Felven1 小时前
华为鲲鹏920s芯片serdestool工具使用说明
linux·运维·服务器·华为·鲲鹏920·serdestool
c2892480843s12 小时前
【鸿蒙心迹】网络请求封装实战:RCP 选型、拦截器与弱网预算
移动开发·网络请求·harmonyos·鸿蒙·rcp
HarmonyOS_SDK17 小时前
【剪贴板体验优化】前置预校验,航旅纵横权限弹窗体验更智能
harmonyos
SoFarAway18 小时前
拆解华为一碰传
华为·逆向
贾伟康18 小时前
【共创稿事节】HarmonyOS 7.0 Progress进度体系:从接入到生产验证
性能优化·实战·harmonyos·arkts·arkui
贾伟康1 天前
HarmonyOS API 26 HMAF Skill 端侧 A2A 消息去重与上下文最小化
harmonyos·arkts·skill·a2a·hmaf
贾伟康1 天前
HarmonyOS API 26 HMAF Skill 多 Skill 冲突仲裁与可恢复执行
harmonyos·arkts·智能体·skill·hmaf