做Android和Ios消息推送一年了,有经验也有教训,抽空总结一下,由于我负责的是服务端的工作,所以偏重服务端的介绍。
Ios推送的原理可以用下图概括:
图中,Provider是指某个iPhone软件的Push服务器。
APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器。
上图可以分为三个阶段。
第一阶段:应用程序把要发送的消息、目的iPhone的标识打包,发给APNS。
第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发到iPhone。
第三阶段:iPhone把发来的消息传递给相应的应用程序, 并且按照设定弹出Push通知。
在实现ios消息推送之前,需要了解两个概念:deviceToken和playload。deviceToken类似于设备的身份证,可以定位app安装的设备。playload是json格式的消息载体,格式是:{"aps":{"sound":"Sent.wav","alert":"","badge":1}},当然也可以自定义参数,但是大小不能超过256个字节。
准备好deviceToken和playload之后,接下来需要与APNS建立SSL连接,APNS提供了两个接口地址,分别是生产环境(gateway.push.apple.com)和测试环境(gateway.sandbox.push.apple.com),端口号都是2195。
当连接建立好之后,把deviceToken,playload和identifier以二进制流的形式发送给ANPS,可以维持该连接,发送多个有效的playload。由于apns是异步的,所以不会立即返回结果。
图一:消息格式
当apns成功接收消息后,不会返回任何信息,只有当apns处理失败时,才会返回错误信息(见图二),第一位是数字8,第二位是状态码,Identifier是消息ID。
图二:错误的信息格式:
开发中应避免每发送一次推送通知就建立、关闭一次连接。频繁的建立、关闭连接可能会被 APNS 认为是 DOS攻击,从而拒绝发送 provider 的推送通知发送请求。
在JAVA中,可以使用第三方开源框架javapns给ios设备推送消息,只需简单几行代码即可实现,避免了复杂的socket连接建立与消息封装。
但是在使用过程中,发现javapns2.2版本两个严重的bug,会造成消息的阻塞和重复发送,目前我们已经修改了javapns的源码,修复了这两个问题。
1.失败消息重发机制:
javapns对于失败消息有个重发机制,假设推送了5条消息,分别是:1,2,3,4,5,如果第3条消息失败了,则javapns会把第3条以后的消息,也就是4,5重新推送。
看源码:
List<PushedNotification> notificationsToResend = new ArrayList<PushedNotification>(); boolean foundFirstFail = false; for (PushedNotification notification : pushedNotifications.values()) { if (foundFirstFail || !notification.isSuccessful()) { if (foundFirstFail) notificationsToResend.add(notification); else { foundFirstFail = true; skippedNotification = notification; }
} }
2.消息的存储机制:
javapns会把所有推送的消息保存在内存中,只有与apns的长连接断开时才会清除这些消息,如果与apns的长连接始终未断开,则这些在内存中的消息是不会被清除掉的。如果推送的消息比较多,日积月累,ios推送服务就出问题了,消息不能正常推送,必须重启消息推送服务。
解决这两个问题的办法需要修改javapns的源码。
在实际使用过程中,我们也发现,如果给apns在短时间内频繁推送相同内容,ios设备的提醒机制就会混乱,这是apns的问题,我们无法解决,只能尽量避免出现这种情况。