
这篇我不想只讲“怎么把推送收下来”而是把我自己做 Push 功能时真正绕不开的几件事讲透Token 怎么管理、消息通道怎么设计、点击通知后怎么准确跳页、离线场景怎么兜住以及为什么很多推送功能看起来能跑上线后却总在链路细节上翻车。一、推送功能真正难的不是“能收到”而是“能稳定闭环”很多人第一次接 Push Kit关注点通常只有一个设备能不能收到消息。真做到业务里问题马上就变了。一个活动通知发出去用户是前台收到还是后台弹系统通知一个订单状态通知过来应该进入订单详情还是先落到消息中心同一个应用里有系统公告、交易提醒、互动消息、运营消息它们到底该不该走同一个消息通道用户关掉某类通知后服务端要不要继续推客户端怎么感知这些都不是“注册个 Token、调个回调”能解决的。我后面把链路拆成四段设备身份建立注册 Push Token形成设备可达身份消息分类落槽按业务类型映射 Notification Slot消息展示与点击前台展示、后台通知、点击回调处理路由执行闭环解析参数、打开目标页、记录跳转结果。做完这四段推送功能才算从“能用”走到“可交付”。二、先把消息通道设计清楚别一上来就堆接口我实际做这类功能时最不建议的写法就是所有消息都塞进一个通知通道里。短期看很省事长期维护会越来越乱。因为消息类型不同优先级、展示方式、可关闭策略、落地页行为都不一样。我更倾向把消息分成四类系统通知公告、版本提醒、安全提醒互动消息评论、点赞、我交易消息订单、支付、物流活动消息营销活动、福利领取、运营触达。通道层只做“分类与承载”不要把复杂业务判断塞进 Notification Slot。Slot 更适合解决“这条消息属于哪一类、该怎么展示、用户能否单独关闭”这类问题。下面这段代码就是一个比较实用的通道初始化思路import notificationManager from kit.NotificationKit; export class NotificationUtil { static async createMessageSlots() { const slots [ { type: notificationManager.SlotType.SOCIAL_COMMUNICATION, name: 互动消息, description: 评论、点赞、我的消息, level: notificationManager.SlotLevel.LEVEL_HIGH, }, { type: notificationManager.SlotType.CONTENT_INFORMATION, name: 系统通知, description: 系统公告与服务提醒, level: notificationManager.SlotLevel.LEVEL_DEFAULT, }, { type: notificationManager.SlotType.SERVICE_INFORMATION, name: 交易消息, description: 订单、支付、物流状态更新, level: notificationManager.SlotLevel.LEVEL_HIGH, } ]; for (const slot of slots) { await notificationManager.addSlot(slot); } } }这里最关键的不是 API 本身而是把消息类别和展示语义提前定住。这样后面无论服务端发什么消息客户端都能用统一规则承接。三、Token 只是起点真正要管的是“设备身份状态”很多线上问题不是消息没发而是 Token 状态出了偏差。常见情况有几个用户重装应用旧 Token 失效用户清理数据后客户端还在用老的注册状态服务端保存了多个历史 Token没有做去重客户端拿到 Token 后没及时回传导致消息发往空设备通知权限关闭了但服务端还按“可触达”处理。所以客户端拿到 Token 后最好立刻做三件事本地保存最新 Token上报业务服务端绑定用户与设备同步当前通知权限与通道开关状态。我一般会把注册和点击监听放在入口页初始化阶段import pushService from kit.PushKit; import { router } from kit.ArkUI; async function initPush() { const token await pushService.registerToken(); AppStorage.setOrCreate(pushToken, token); console.info(token registered: ${token}); pushService.onMessage((msg) { console.info(message received, id${msg.messageId}); }); pushService.onNotificationClick((msg) { console.info(notification clicked, route${msg.routeUrl}); if (msg.routeUrl) { router.pushUrl({ url: msg.routeUrl }); } }); }这段逻辑很朴素但它解决了两个真实问题Push Token 不再只是“拿到了就算完”而是进入应用状态体系点击通知不再只是“知道点了”而是直接进入路由执行阶段。上面这类 DevEco 截图的价值不在于“界面看起来像不像”而是它把一条完整链路放在一个视角里左边是工程目录中间是 Push 初始化和点击回调代码右边是模拟器中的消息中心底部日志又把 Token 注册、消息接收、点击跳转串起来了。四、消息中心页面怎么设计决定了后续排查效率我现在做推送类功能会尽量给业务留一个“消息中心”或“推送调试页”。不是为了好看是为了排查快。一个可用的消息中心我会至少放这些信息通知总开关消息通道列表Token 状态通知权限状态最近消息列表点击后跳转明细。因为用户一句“我没收到消息”背后可能是五种完全不同的问题通知权限没开某个 Slot 被关了Token 未注册成功消息已经展示但被忽略点击后路由参数解析失败。把这些状态可视化很多问题根本不用进代码就能先排一半。这张图里我比较喜欢的是红色标注做法。它不是为了“装饰”而是把用户看不见但开发必须关注的几个关键点直接圈出来消息通道到底是哪类消息在生效Token 状态设备是否处于可达状态通知权限系统层是否允许投递。这种标法很适合放文章里因为它兼顾了读者阅读效率和排查思路。五、点击通知之后不要只跳页要把“路由链路”跑完整很多推送功能的坑出在点击通知之后。最常见的误区是只要点开了某个页面就算链路完成。其实不够。推送点击至少应该校验消息 ID 是否唯一可追踪路由参数是否完整目标页面是否匹配页面是否成功打开是否有失败兜底。我自己的习惯是消息体里放三层信息业务类型如 order / activity / system目标页面如/pages/order/detail路由参数如订单 ID、来源标记、utm 等。收到点击回调后先落日志再统一交给路由解析器处理。这样消息来源一多也不会把点击逻辑写散。这张图很适合拿来讲“点击闭环”。红色圈和箭头标出的几个字段基本就是线上排查最有价值的几个观察点消息 ID唯一标识一条消息路由参数点击之后到底要带什么信息走点击后跳转结果最终有没有真正落到目标页面。如果文章只讲“点击跳详情页”其实很浅。把这几个观察位讲清楚读者才能真正把推送能力落到业务里。六、我最后总结的 Push 实战经验这套链路做下来我越来越确信一件事Push 功能最有技术含量的地方不是调用哪个接口而是把消息触达、展示控制和点击跳转串成一条可验证的业务链。我现在会默认做这些约束Token 注册后必须立即回传服务端消息必须带类型、目标页和参数不同消息走不同 Notification Slot客户端必须能查看权限、通道、Token 状态点击通知后必须记录最终路由结果异常场景要有统一兜底页或消息中心回退。这样做的好处很直接平时看起来只是多写了一点结构代码到了线上排查阶段会比“哪里坏了再补哪里”轻松很多。如果你现在也在做 HarmonyOS 推送能力我的建议不是“先把消息发出去”而是先把链路设计完整再让消息跑起来。一旦链路对了后面的扩展比如多业务线推送、AB 实验、活动消息降级、通知聚合才有稳定基础。