1. 为什么你的UniPush离线推送总失败?
很多开发者跟我吐槽:"明明按照文档配好了UniPush,测试时在线推送能收到,但用户手机一锁屏推送就石沉大海。" 这其实就是典型的离线推送失效问题。我去年接手的一个充电类APP就遇到过同样情况——用户充电完成后,APP在后台时根本收不到推送通知,差点被应用商店下架。
离线推送的本质是当APP进程被杀死时,由手机厂商的系统级推送服务来接管消息分发。但各厂商的推送通道就像不同国家的海关,都有自己的特殊通关文牒要求。华为要"报关单",小米要"通行证",vivo要"货物清单",缺了任何一项都会被拦截。
2. 厂商通道参数全解析
2.1 华为通道(HW)的快递标签
华为推送有个特别的设计——消息分类系统。就像快递要区分是普通包裹还是生鲜冷链,华为要求每条推送必须声明消息类型。实测发现,如果漏掉这个参数,华为设备上离线推送成功率直接掉到30%以下。
关键配置项:
"HW": { "/message/android/category": "EXPRESS" }可选值包括:
EXPRESS即时消息(如订单状态变更)VOICE语音类通知SOCIAL社交互动消息
去年我们有个电商项目就栽在这里。当时所有华为手机用户都收不到订单发货通知,排查三天才发现是没填category字段。补上这个参数后,推送成功率立刻提升到98%。
2.2 vivo通道(VV)的订单分类
vivo的通道参数更像是在给消息打标签。他们的系统会根据/category字段对推送进行分级处理。如果不指定这个值,vivo设备每天最多只能接收5条离线推送——这对大多数APP来说根本不够用。
正确配置示例:
"VV": { "/category": "ORDER" }常见业务场景对应的分类:
ORDER交易订单类SOCIAL社交互动类SYSTEM系统通知类
有个坑要注意:vivo的文档里写着这个字段"非必填",但实际测试发现不填就会被限流。我们曾用10台vivo设备做压力测试,不填category的设备在第6条推送时就全部失效了。
2.3 小米通道(XM)的通行证
小米的机制最特殊——要求开发者必须提前申请channel_id。这个过程就像办签证,需要提交材料审核。很多开发者卡在这一步,因为小米审核可能需要1-3个工作日。
申请步骤:
- 登录小米开放平台
- 进入"推送运营"→"通道管理"
- 新建通道(填写APP包名和用途说明)
- 等待审核通过
代码中这样使用:
"XM": { "/extra.channel_id": "MI_APPROVED_CHANNEL_ID" }去年我们有个健身APP就吃过亏。上线前一天才发现小米推送没配置,紧急申请通道又遇到周末,最后不得不临时关闭小米设备的离线推送功能。建议大家在开发初期就先把各厂商通道申请好。
2.4 OPPO通道的邮件申请
OPPO的流程最复杂,需要邮件申请私信通道。很多开发者在这里放弃治疗,但其实只要按标准流程走,2天内就能搞定。
完整申请流程:
- 登录OPPO开放平台
- 下载《OPPO推送通道申请表》
- 填写APP信息和使用场景
- 发送邮件至push@oppo.com
- 收到包含
Channel_ID的回复邮件
配置示例:
"OPPO": { "/channel_id": "OPPO_APPROVED_ID" }特别提醒:OPPO对推送内容审核最严格。我们有个新闻APP就因推送内容含敏感词被永久封禁通道,后来重新注册企业账号才解决。
3. 云函数推送模板大公开
下面这个云函数模板集成了四大厂商的全部特殊参数,已经在我们7个上线项目中验证过稳定性:
const uniPush = require('unipush-sdk'); exports.main = async (event, context) => { // 构造推送消息体 const pushPayload = { cids: event.deviceTokens, title: event.title, content: event.content, data: event.customData, options: { VV: { '/category': event.category || 'SYSTEM' }, HW: { '/message/android/category': 'EXPRESS' }, XM: { '/extra.channel_id': process.env.XIAOMI_CHANNEL_ID }, OPPO: { '/channel_id': process.env.OPPO_CHANNEL_ID } }, request_id: Date.now().toString() }; try { const result = await uniPush.send(pushPayload); return { success: true, data: result }; } catch (error) { console.error('推送失败:', error); return { success: false, error: error.message }; } };使用技巧:
- 将厂商通道ID放在环境变量中(process.env),避免硬编码
request_id用时间戳生成,确保每次推送都是唯一请求- 通过event参数动态传入设备token和推送内容
这个模板最精髓的部分在于options字段的动态处理。我们通过event.category让调用方可以指定vivo的消息分类,同时给其他厂商设置了合理的默认值。
4. Postman测试全流程指南
用Postman测试时,90%的失败都是因为Body格式不对。这是我用坏三个回车键才总结出的正确姿势:
设置Headers:
- Content-Type: application/json
- Authorization: Bearer your_api_key
请求体示例:
{ "cids": ["测试设备token"], "title": "充电完成通知", "content": "您的电动车已充电完成,请及时拔除电源", "data": { "order_id": "123456", "station_id": "A12" }, "options": { "VV": { "/category": "ORDER" }, "HW": { "/message/android/category": "EXPRESS" }, "XM": { "/extra.channel_id": "你的小米通道ID" }, "OPPO": { "/channel_id": "你的OPPO通道ID" } }, "request_id": "1621234567890" }常见测试陷阱:
- 忘记加
options字段:表现为在线能收离线收不到 cids写成字符串而非数组:直接报参数错误- 重复使用
request_id:第二次请求会被视为重复推送 - 厂商参数拼写错误:比如华为的
category写成categroy
建议在测试阶段开启uniPush的详细日志,我们就是通过日志发现某个华为设备的category值被错误覆盖的问题。
5. 避坑指南:血泪经验总结
厂商限制策略:
- 华为:未分类消息限流1000条/天
- vivo:未分类消息每天最多5条/设备
- OPPO:内容违规直接封禁通道
- 小米:未配置channel_id直接拒绝
参数优先级陷阱: 当
title和content与厂商后台配置的模板冲突时,部分厂商会优先使用模板内容。我们在vivo设备上就遇到过推送显示"默认通知内容"的问题,后来在vivo开发者后台关闭模板匹配才解决。通道生效延迟: 新申请的OPPO通道可能要2小时才能生效。有次我们半夜上线新功能,测试失败后排查到凌晨才发现是OPPO缓存延迟。
设备注册时机: 部分华为设备需要用户手动开启"允许自启动"才能注册推送通道。我们的解决方案是在APP启动时检查通道状态,如果未注册就弹窗引导用户设置。
厂商文档过时: vivo的文档去年更新过三次参数格式,最稳妥的方式是直接问他们的技术支持要最新版API文档。我们现在维护着一个各厂商最新参数的对照表,每月都会检查更新。