1, 前言
本文要分享的消息推送指的是当 iOS 端 APP 被关闭或者处于后台时, 还能收到消息 / 信息 / 指令的能力.
这种在 APP 处于后台或关闭情况下的消息推送能力, 通常在以下场景下非常有用:
1)IM 即时通讯聊天应用: 聊天消息通知, 音视频聊天呼叫等, 典型代表有: 微信, QQ, 易信, 米聊, 钉钉, Whatsup,Line;
2)新闻资讯应用: 最新资讯通知等, 典型代码有: 网易新闻客户端, 腾讯新闻客户端;
3)SNS 社交应用: 转发 / 关注 / 赞等通知, 典型代表有: 微博, 知乎;
4)邮箱客户端: 新邮件通知等, 典型代表有: QQ 邮箱客户端, Foxmail 客户端, 网易邮箱大师;
5)金融支付应用: 收款通知, 转账通知等, 典型代表有: 支付宝, 各大银行的手机银行等;
.... ....
除了以上典型场景下, 消息推送这种能力已经被越来越多的 APP 作为基础能力之一, 因为移动互联网时代下, 用户的 "全时在线" 能力非常诱人和强大, 能随时随地即时地将各种重要信息推送给用户, 无疑是非常有意义的.
众所周之, iOS 端的这项消息推送能力就是使用苹果提供的 APNs 服务来实现(有些 iOS 小白开发者可能看到各种第 3 方的 iOS 端消息推送 SDK, 总会习惯性地认为这是完全由第 3 方提供的能力, 实际上同样是使用 APNs, 只是封装了一下而已). 目前介绍 APNs 消息推送的文章多讨论的是手机端的实现, 而服务端的消息要怎么 "推" 出来这样的文章, 要么太老, 要么只是介绍如何调用第 3 方的服务端 SDK 接口而已(如极光推广, 友盟推送, 腾讯信鸽推送等). 所以本文趁着最近对项目组的老苹果 iOS 推送进行升级修改机会, 详细查阅了最新苹果的 APNs 接口文档, 同时为了避免重复造轮子(懒), 在调研了一些开源常用的库之后, 选择了 Turo https://turo.com/ 团队开发和维护的 pushy 开源工程 https://github.com/relayrides/pushy 来实现在 Java 服务端调用苹果最新的 APNs HTTP/2 接口进行消息推送, 并借此文对 Pushy 的使用方法进行了总结和记录, 希望对你用.
补充说明: 网上目前能查到的有关 iOS 端 APNs 消息推送的 Java 服务端代码实现, 多是介绍如何使用 Java-APNS https://github.com/notnoop/java-apns 这个工程, 但这个工程以及类似的其它工程都很久没有维护了, 跟最新的苹果 APNs 服务已经很难匹配了. 相较而言 puhsy 这个工程一直比较活跃, 也对苹果的最新 APNs 跟进的比较及时, 因而本文作者在公司的项目进行升级和重构过程中, 毫不犹豫的使用了 pushy.
学习交流:
- 即时通讯开发交流 3 群: 185926912 http://shang.qq.com/wpa/qunwpa?idkey=051ed62f79e15485c9b5af622dd5b1c87b26bedb890b068be79dd2006fc80ccf [推荐]
- 移动端 IM 开发入门文章:新手入门一篇就够: 从零开发移动端 IM http://www.52im.net/thread-464-1-1.html
- publicclassIOSPush {
- privatestaticfinalLogger logger = LoggerFactory.getLogger(IOSPush.class);
- privatestaticfinalApnsClient apnsClient = null;
- privatestaticfinalSemaphore semaphore = newSemaphore(10000);
- publicvoidpush(finalList deviceTokens, String alertTitle, String alertBody) {
- longstartTime = System.currentTimeMillis();
- if(apnsClient == null) {
- try{
- EventLoopGroup eventLoopGroup = newNioEventLoopGroup(4);
- apnsClient = newApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)
- .setClientCredentials(newFile("/path/to/certificate.p12"), "p12-file-password")
- .setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();
- } catch(Exception e) {
- logger.error("ios get pushy apns client failed!");
- e.printStackTrace();
- }
- }
- longtotal = deviceTokens.size();
- finalCountDownLatch latch = newCountDownLatch(deviceTokens.size());
- finalAtomicLong successCnt = newAtomicLong(0);
- longstartPushTime = System.currentTimeMillis();
- for(String deviceToken : deviceTokens) {
- ApnsPayloadBuilder payloadBuilder = newApnsPayloadBuilder();
- payloadBuilder.setAlertBody(alertBody);
- payloadBuilder.setAlertTitle(alertTitle);
- String payload = payloadBuilder.buildWithDefaultMaximumLength();
- finalString token = TokenUtil.sanitizeTokenString(deviceToken);
- SimpleApnsPushNotification pushNotification = newSimpleApnsPushNotification(token, "com.example.myApp", payload);
- try{
- semaphore.acquire();
- } catch(InterruptedException e) {
- logger.error("ios push get semaphore failed, deviceToken:{}", deviceToken);
- e.printStackTrace();
- }
- finalFuture> future = apnsClient.sendNotification(pushNotification);
- future.addListener(newGenericFutureListener>() {
- @Override
- publicvoidoperationComplete(Future pushNotificationResponseFuture) throwsException {
- if(future.isSuccess()) {
- finalPushNotificationResponse response = future.getNow();
- if(response.isAccepted()) {
- successCnt.incrementAndGet();
- } else{
- Date invalidTime = response.getTokenInvalidationTimestamp();
- logger.error("Notification rejected by the APNs gateway:"+ response.getRejectionReason());
- if(invalidTime != null) {
- logger.error("\t...and the token is invalid as of"+ response.getTokenInvalidationTimestamp());
- }
- }
- } else{
- logger.error("send notification device token={} is failed {}", token, future.cause().getMessage());
- }
- latch.countDown();
- semaphore.release();
- }
- });
- }
- try{
- latch.await(20, TimeUnit.SECONDS);
- } catch(InterruptedException e) {
- logger.error("ios push latch await failed!");
- e.printStackTrace();
- }
- longendPushTime = System.currentTimeMillis();
- totalcost= "+ (endPushTime - startTime) +", pushCost=" + (endPushTime - startPushTime));
- }
- }
来源: http://www.jianshu.com/p/03d5a112538e