UniPush 2.0 实战:从零到一,构建基于云函数的APP推送系统
1. 为什么选择UniPush 2.0 + 云函数方案?
去年我接手一个电商APP项目时,老板突然要求三天内上线消息推送功能。当时试了各种第三方推送服务,不是收费太高就是集成太复杂,最后用UniPush 2.0配合云函数,居然只用半天就搞定了推送闭环。这种组合方案最大的优势就是开发成本极低——你不需要自己搭建推送服务器,也不用处理复杂的消息队列,甚至连Android和iOS的差异都被封装好了。
具体来说,这套方案有三大杀手锏:
- 零运维成本:传统方案需要自己维护消息队列服务器,而云函数自动扩缩容
- 跨平台统一API:Android/iOS/小程序用同一套代码发送推送
- 按量付费:没有设备数限制,发送百万条推送的成本可能不到一杯奶茶钱
实测下来,从注册到发送第一条推送消息,新手开发者完全可以在1小时内跑通全流程。不过要注意的是,虽然UniPush 2.0理论支持小程序,但实际项目中更推荐用微信原生的订阅消息,这个我们后面会详细对比。
2. 前期准备:避坑指南
2.1 服务空间创建那些事儿
第一次用uniCloud时,我在服务商选择上栽过跟头。虽然官方文档说支持多家云服务商,但实测发现阿里云的体验最稳定(关键是有免费试用期)。具体操作时要注意:
- 打开uniCloud控制台
- 点击"创建服务空间",地区建议选"华东2(上海)"
- 一定要勾选"启用uniPush 2.0"选项,这个默认是不开启的
注意:免费版服务空间有每天1000次云函数调用限制,上线前记得升级到付费版
2.2 权限配置的隐藏关卡
很多新手会卡在APP的推送权限申请环节。特别是Android机型,从8.0开始通知权限变得异常复杂。这里分享一个万能检测方案:
function checkNotificationPermission() { // #ifdef APP-PLUS if (plus.os.name == 'Android') { const main = plus.android.runtimeMainActivity(); const NotificationManagerCompat = plus.android.importClass( "androidx.core.app.NotificationManagerCompat" ); return NotificationManagerCompat.from(main).areNotificationsEnabled(); } else { const UIApplication = plus.ios.importClass('UIApplication'); const app = UIApplication.sharedApplication(); const settings = app.currentUserNotificationSettings(); return settings.types > 0; } // #endif return true; }当检测到权限未开启时,建议用这段代码引导用户跳转设置页:
uni.showModal({ title: '温馨提示', content: '开启通知权限后,您将及时收到订单状态变更等重要消息', success(res) { if (res.confirm) plus.runtime.openSettings(); } });3. 云函数开发实战
3.1 消息发送核心代码解析
在cloudfunctions目录新建pushMessage云函数,这个是我优化过的生产级代码:
'use strict'; const uniPush = uniCloud.getPushManager({ appId: "__UNI__XXXXXX" // 替换成你的DCloud应用ID }); exports.main = async (event) => { const { cids, // 设备CID数组 title, // 通知标题 content, // 通知内容 payload, // 透传数据 options // 厂商通道配置 } = event; // 参数校验 if (!Array.isArray(cids) || cids.length === 0) { throw new Error('设备CID不能为空'); } try { const res = await uniPush.sendMessage({ push_clientid: cids, title, content, payload: JSON.stringify(payload || {}), force_notification: true, request_id: Date.now().toString(), options: { HW: { // 华为通道特殊配置 "/message/android/importance": "HIGH", ...options?.HW }, ...options } }); return { code: 0, data: res }; } catch (e) { console.error('推送失败:', e); return { code: -1, message: e.message }; } };几个关键点说明:
force_notification: true确保消息始终以通知形式展示request_id用时间戳生成,避免重复消息被去重- 华为通道需要单独配置importance参数才能保证高优先级
3.2 云函数调试技巧
上传云函数后,在uniCloud web控制台可以测试调用。我建议先用这个测试JSON:
{ "cids": ["测试设备CID"], "title": "测试标题", "content": "测试内容", "payload": { "page": "/pages/order/detail", "id": "123" } }如果返回{"code":0}但手机没收到推送,八成是CID有问题。这时候可以:
- 在App的onLaunch里打印CID:
uni.getPushClientId({ success: (res) => console.log('当前设备CID:', res.cid) });- 确保手机和测试用的CID完全一致
4. 客户端完整接入方案
4.1 初始化监听逻辑
在App.vue的onLaunch里添加这些代码,处理消息到达和点击事件:
// 监听推送消息到达 plus.push.addEventListener("receive", (msg) => { console.log('[Push] 收到消息:', msg); if (msg.type === 'receive') { uni.$emit('pushNotification', { title: msg.title, content: msg.content, payload: tryParse(msg.payload) }); } }); // 处理通知栏点击 plus.push.addEventListener("click", (msg) => { const payload = tryParse(msg.payload); if (payload.page) { setTimeout(() => { uni.navigateTo({ url: payload.page }); }, 300); // 解决冷启动时页面未加载的问题 } }); function tryParse(str) { try { return JSON.parse(str); } catch { return str; } }4.2 厂商通道适配经验
不同手机厂商的推送通道各有脾性,这是我踩坑后总结的配置表:
| 厂商 | 关键配置项 | 注意事项 |
|---|---|---|
| 华为 | importance等级设为HIGH | 必须配置category字段 |
| 小米 | extra.channel_id | 需要在小米开放平台申请 |
| VIVO | /category | 需要配置通知分类 |
| OPPO | 无特殊要求 | 通道申请较严格 |
建议在云函数的options参数里这样配置:
const options = { VV: { "/category": "IM" }, HW: { "/message/android/category": "IM" }, XM: { "/extra.channel_id": "your_channel_id" } };5. 性能优化与监控
5.1 批量推送策略
当需要给大量用户发送推送时(比如促销活动),直接循环调用云函数会触发限流。这时应该:
- 使用uniCloud.sendSms的批量接口
- 每次请求最多包含1000个CID
- 添加失败重试机制
优化后的代码结构:
async function batchPush(messages) { const BATCH_SIZE = 1000; const results = []; for (let i = 0; i < messages.length; i += BATCH_SIZE) { const batch = messages.slice(i, i + BATCH_SIZE); try { const res = await uniCloud.callFunction({ name: 'batchPush', data: { messages: batch } }); results.push(...res.result); } catch (e) { console.error(`批次${i}推送失败:`, e); // 加入重试队列 } } return results; }5.2 推送效果监控
在云函数里添加埋点代码,记录推送到达率:
const db = uniCloud.database(); await db.collection('push_logs').add({ cids: event.cids, title: event.title, send_time: Date.now(), status: 'sent' });然后在客户端收到消息时上报状态:
plus.push.addEventListener("receive", (msg) => { uniCloud.callFunction({ name: 'reportPushStatus', data: { cid: plus.push.getClientInfo().clientid, msg_id: msg.msgid, receive_time: Date.now() } }); });这套方案在我们日活10万+的电商APP上稳定运行了半年,到达率始终保持在95%以上。最关键的是,当推送出现问题时,能快速定位是服务端发送失败,还是客户端接收失败。
