摘要: 本地通知不显示、推送 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 单/周(权限与渠道前置校验) |
避坑要点
- 先检查通知权限:用户可能关闭
- 点击跳转双入口 :
onCreate接冷启动、onNewWant接后台点击,只写一处会漏场景 - 合并通知:频繁发被限流
边界与已知限制
| 限制项 | 具体表现 | 规避方式 |
|---|---|---|
| 通知权限 | 需用户授权,首次拒绝后系统不再弹窗 | 提供引导入口跳转设置页开启 |
| 渠道关闭 | 用户可单独关闭某个渠道,关闭后静默无提示 | 发送前检查渠道开关状态并引导 |
| token 刷新 | 推送 token 会变化,未及时上报导致推送失败 | 每次启动上报,并监听刷新回调 |
| 到达率 | 依赖厂商通道,受省电策略与网络影响,非 100% | 关键业务用服务端长连接或轮询兜底 |
| 频率限制 | 短时间大量通知被限流 | 同类通知合并,按业务分级发送 |
| 后台跳转 | 应用在后台时点击走 onNewWant,不走 onCreate |
两处都要解析 want 参数 |
| 进程被杀 | 本地通知仍可触发,但内存状态已丢失 | 通知携带足够参数,避免依赖内存态 |
小结
通知链路有两段:本地通知(emitter/系统通知)和远程推送(厂商通道),踩坑点完全不同------本地主要卡权限与点击跳转,远程主要卡 token 上报与到达率。
落地时最容易漏的是 onNewWant:应用在后台时点击通知不会走 onCreate,只在这里解析参数的话,冷启动能跳、热启动跳不了。两个入口都要处理同一套 want 解析逻辑。
评论区交流: 你的通知集成遇到什么问题?评论区交流。