开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第28天,点击查看活动详情
开始
app被关闭后,无法再创建通知栏消息,好消息是各家手机厂商都提供了离线消息推送的接口,坏消息是只有国内的部分手机厂商有,覆盖率不足,国外的、杂牌的、小众的无法实现离线个推。
每家手机厂商的离线推送接口都不同,uniPush已经帮我们封装好了,免费的功能几乎可以满足日常开发了。
个推需要服务端的配合才能完成: 开发前必读-个推文档中心 (getui.com)
个推的原理:
启动app后,app会与个推服务器进行长连接,这个长连接用来检查app是否在线,以及在线时收消息
每次向个推发送消息,会检查app是否运行中:
- app在线时(长连接正常),个推会走自己的服务,消息不经过厂商;
- app离线时,消息会发给手机厂商帮你创建通知栏
判断app是不是在运行,是个推做的,不是厂商。
是否在线和网络有关系,长连接断开后,几分钟后(或者切换网络时)会尝试重新连接,所以有时候会出现app在运行,却收到了离线消息的奇怪现象
点击通知栏消息,会唤醒app并传递参数,app需要在App.vue中判断参数并决定跳转到哪里。
遇到的问题:
本来希望使用个推,代替scoket聊天的,但是在上线后发生了一些问题无法解决:
在小米手机中,偶尔会出现app使用过程中,突然离线的情况,且切换了网络也无法重连,咨询了个推客服,其他人貌似没有遇到过这样的情况,由于不知道掉线原因,以及无法主动与个推服务器重连,我们决定只使用个推进行离线推送,在线消息仍通过scoket收发。
离线个推结合在线scoket是目前处境的最优解,但缺点也很明显:app状态可能不一致,scoket掉线不代表个推处于离线状态,个推掉线不代表scoket掉线,这部分差异只能在代码中进行优化,以减少两者差异。
接入个推
1、在manifest.json中,勾选 Push → uniPush
2、在 应用列表 (dcloud.net.cn) 中,找到对应的应用(浏览器登录的账号是应用的创建者),填入向各大厂商申请的离线推送资料等
3、获取客户端设备id,通过接口交给服务端保存:
plus.push.getClientInfo().clientid
这个clientid(后续简称cid),是客户端离线推送的唯一标识,如果你要发的是通用消息,所有用户都能收的到,那这个cid应该在app第一次打开的时候就保存到服务器;相反,如果这个消息是私聊消息,应该在用户登录的时候将cid和用户id进行推送,服务端在发私人消息的时候,只推送到对应的cid,在用户退出登录后,应解除cid的绑定
4、接受个推在线消息(我们使用了scoket代替了个推在线消息,不实现这一步,个推就不会影响到我们在线运行)
//监听接收透传消息事件
plus.push.addEventListener('receive', function(msg){
//处理透传消息的业务逻辑代码
if ( msg.aps ) { // 处理ios死循环
//APNS下发的消息,应用在前台
} else {
//其它情况接收消息
}
}, false);
收到消息后,我们可能会创建通知栏提醒用户,但是在ios中,客户端创建通知栏也会触发receive事件,receive事件中如果再创建通知栏,就会形成死循环,导致app卡死,解决方法可以:
- 判断msg.aps,如果为false,才会创建通知栏
- 客户端创建通知栏时,携带的数据加上标识,在receive事件中,判断如果是本地创建的就不处理。(原理和上面一样,只是自己制定标识)
5、创建通知栏
文档:HTML5+ API Reference (html5plus.org)
plus.push.createMessage('消息文本', {content: '携带的参数'});
6、处理点击通知栏响应
// 应该在App.vue中的onLaunch生命周期中绑定该事件
plus.push.addEventListener('click', (message) => {
try{
handlePush(message)
}catch(e){
//TODO handle the exception
console.error(e,"点击通知栏消息失败")
}
});
message.payload 是通知携带的数据,这个规则应该前后端商量统一。
7、打包、测试
个推消息必须打包安装才能看到效果,可以在receive和click事件中,通过 uni.showModal 打印查看