博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java整合APNS
阅读量:2068 次
发布时间:2019-04-29

本文共 7395 字,大约阅读时间需要 24 分钟。

序言

这里主要从代码层面展示下,如何整合APNS。并用该demo进行远程消息的发送

参考资料:

  •                           3
  •                              2
  •                                                         1

 

整体流程

  1. App在苹果的开发者系统上注册获取证书(这个证书可以用于生成.p12 cuiyaonan2000@163.com
  2. 某一个苹果手机用户注册到APNS,APNS将注册的token返回给APP.
  3. APP将收到的token返还给我们的后台服务
  4. 后台服务连接APNS,获取连接对象
  5. 后台服务构建消息载体
  6. 后台通过连接对象,根据指定的token将信息发送给指定的手机用户

 

连接APNS的方式

后台服务连接APNS有两种方式----------------连接方式主要是用于安全连接,以及告诉APNS你是哪个APP.cuiyaonan2000@163.com

  • 基于Token。使用开发者中心申请的.p8文件和Key ID进行Token认证
  • 基于推送证书,使用.p12证书认证--------------使用居多(cuiyaonan2000@163.com)

.p8 和.p12 的生成(这个是由app人员生成后告诉后台开发,用于连接APNS)

.p8的生成

.p8 文件生成:登录开发者账号,进入Certificates, Identifiers & Profiles 面板,左侧菜单栏选中Keys ,点击右侧 + 号按钮,生成如图文件创建页面,,输入文件名,勾选APNs,点击continue,直到生成.p8文件

.p12(这个操作有点复杂交给别人去弄吧)

.p12 文件生成 : 在系统软件keychain 中,找到你的推送证书(开发的或者生产的),然后双击证书,弹出如图窗口,选择Export(导出)证书,选择存放的位置,就生成的.p12 文件

 

远程通知的组成

当创建了一个APNSClient实例之后,你就可以连接上APNs网关服务器可。

这个连接过程是异步的;client实例将会马上返回一个Future对象,但在你发送任何推送通知之前,你需要等待这个连接过程完成之后才能进行。-----------------------(这个就是响应式编程,我们在微服务中也会用到cuiyaonan2000@163.com)

请注意,这个对象是Netty框架中的,它是Java中的接口的一个拓展,它允许调用者添加监听器或添加methods来检测Future的状态。

  1. 目标设备的token
  2. 代表app签名的topic
  3. 通知负载(消息内容)

 

推送返回的Future

Future对象,但在货值推送消息是被APNS网关服务器接受还是拒绝之前,还需要等待Future过程完成。-------响应式编程

Future对象的执行结果会有以下三种情形:

  1. APNs网关服务器接收推送通知,并尝试将消息投递到token对应的目标设备。
  2. APNs网关服务器拒绝推送通知,并且这是一个永久的错误,您的推送通知将不会被重新投递推送。此外,APNs服务器会给token对应的设备标记一个最近失效时间的。如果发生这个情况,你应该停止尝试向这个token对应的设备发送任何通知, 除非这个token在这个时间戳之后又重新上线了。-------------------这个后台如何获取该设备什么时候上线呢?????? 否则没法发送远程通知
  3. Future对象因为一些未知异常而执行失败,这通常是在某种特定情况下的暂时性的失败,调用者应该在问题解决之后对这个推送通知进行重新地投递发送。但如果是在这种情况下投递失败,调用者应该通过调用阻塞方法来返回一个Future对象,这个过程其实是在连接断开之后进行自动重连。---------------------------该阻塞方法同时也用于异常出现的格式化返回,比如返回一个错误的自定义信息,以及详细的内容

 

 

Future的效率问题----用监听的方式获取返回结果更适合显示情况

在实际应用场景中,去等待对每个特定设备的阻塞推送,效率是极其低的。而在Future中添加一个监听器,在推送完成之后回调这个监听器的方法,和阻塞等待每个推送完成这种方式相比起来,显然能够提供更高效的高并发服务。

 

Java的实现方式

  1. 使用pushy
  2. 使用原生的代码实现苹果的APNS协议

APNS协议

     APNs协议是基于的一套推送协议。HTTP/2是一套相对新的协议,以至于它的发展还没有延伸拓展到Java世界中。例如以下几点:

  1. HTTP/2依赖于,它是一种在TLS协议上拓展出来的协议协商机制。在目前还没有哪个Java版本原生支持ALPN协议。所以如果我们要用ALPN,目前可以在Java7或Java8中,使用,或者使用。
  2. HTTP/2还需要使用,这个直到Java8才被引入到JDK中。在Java7中最好是使用原生的SSL provider。但在Java8中,原生SSL provider不再是必备的了,但可能还是有一些性能上的不足。

通常来说,原生的SSL provider是满足HTTP/2对系统性能增强要求的最好方法,因为安装是相当简单的,并且它运行在Java 7以上版本通常能够提供比JDK的SSL provider更好的SSL执行性能。

 

pushy使用(最新版本要求JDK8)

官方网址:

 

创建连接

p12连接

final ApnsClient apnsClient = new ApnsClientBuilder()        .setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)        .setClientCredentials(new File("/path/to/certificate.p12"), "p12-file-password")        .build();

p8连接

final ApnsClient apnsClient = new ApnsClientBuilder()        .setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)        .setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File("/path/to/key.p8"),                "TEAMID1234", "KEYID67890"))        .build();

创建消息体

如下我们不用自己拼接json,可以使用pushy的工具,当然也可以自己拼接json

final SimpleApnsPushNotification pushNotification;{    final ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder();    payloadBuilder.setAlertBody("Example!");    final String payload = payloadBuilder.build();    final String token = TokenUtil.sanitizeTokenString("
"); pushNotification = new SimpleApnsPushNotification(token, "com.example.myApp", payload);}

APNS消息体格式(适用于不适用pushy,而是想自己编辑JSON)

如下两个合并着看,然后自己多试试

 

Future的处理

如下的代码示例包含,1发送消息 2获取future 3对future进行控制 4对future进行监听  很重要哦cuiyaonan2000@163.com

//发送消息回复返回值final PushNotificationFuture
> sendNotificationFuture = apnsClient.sendNotification(pushNotification);//对返回值进行处理控制try { final PushNotificationResponse
pushNotificationResponse = sendNotificationFuture.get(); if (pushNotificationResponse.isAccepted()) { System.out.println("Push notification accepted by APNs gateway."); } else { System.out.println("Notification rejected by the APNs gateway: " + pushNotificationResponse.getRejectionReason()); pushNotificationResponse.getTokenInvalidationTimestamp().ifPresent(timestamp -> { System.out.println("\t…and the token is invalid as of " + timestamp); }); }} catch (final ExecutionException e) { System.err.println("Failed to send push notification."); e.printStackTrace();}//监听--用以提高效率sendNotificationFuture.whenComplete((response, cause) -> { if (response != null) { // Handle the push notification response as before from here. } else { // Something went wrong when trying to send the notification to the // APNs server. Note that this is distinct from a rejection from // the server, and indicates that something went wrong when actually // sending the notification or waiting for a reply. cause.printStackTrace(); }});

 

Demo

package cui.yao.nan.apns;import java.io.File;import java.util.Date;import java.util.UUID;import com.turo.pushy.apns.ApnsClient;import com.turo.pushy.apns.ApnsClientBuilder;import com.turo.pushy.apns.DeliveryPriority;import com.turo.pushy.apns.PushNotificationResponse;import com.turo.pushy.apns.PushType;import com.turo.pushy.apns.util.SimpleApnsPushNotification;import com.turo.pushy.apns.util.concurrent.PushNotificationFuture;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;public class APNsUtils {		private static ApnsClient apnsClient = null;	public static void main(String[] args) throws Exception {				// IOS等终端设备注册后返回的DeviceToken		String deviceToken = "5d7cf67dd6ab56c6d699f86806682e7d99e019216df734f4c57196b84790b718";				// 这是你的主题,大多数情况是bundleId,voip需要在bundleId加上.voip。对应文档中的apns-topic		// 代表app签名的topic		String topic = "com.voip.test.voip";				String payload = "{ \"aps\" : {\"alert\" : \"测试\", \"sound\" : \"default\", \"badge\" :1},\"cuiyaonan2000\":\"cuiyaonan2000\" }";				// 有效时间		Date invalidationTime = new Date(System.currentTimeMillis() + 60 * 60 * 1000L);				// 发送策略 apns-priority 10为立即 5为省电		DeliveryPriority priority = DeliveryPriority.IMMEDIATE;				// 推送方式,主要有alert,background,voip,complication,fileprovider,mdm		PushType pushType = PushType.VOIP;				// 推送的合并ID,相同的 apns-collapse-id会在App中合并		String collapseId = UUID.randomUUID().toString();				// apnsId 唯一标示,如果不传,APNs会给我们生成一个		UUID apnsId = UUID.randomUUID();				// 构造一个APNs的推送消息实体		SimpleApnsPushNotification msg = new SimpleApnsPushNotification(deviceToken, topic, payload, invalidationTime,				priority, pushType, collapseId, apnsId);				// 开始推送		PushNotificationFuture
> future = getAPNSConnect() .sendNotification(msg); PushNotificationResponse
response = future.get(); System.out.println(response.getRejectionReason()); // 如果返回的消息中success为true那么成功,否则失败! // 如果失败不必惊慌,rejectionReason字段中会有失败的原因。对应官网找到原因即可 // https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/handling_notification_responses_from_apns?language=objc System.out.println("------------->" + response); } public static ApnsClient getAPNSConnect() { if (apnsClient == null) { try { // 四个线程 EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4); apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST) .setClientCredentials(new File("/Users/liguoxin/Desktop/p12/distri_push.p12"), "111111") //setConcurrentConnections用于设置服务器与苹果服务器建立几个链接通道,这里是建立了四个,链接通道并不是越多越好的,具体速度自己百度 .setConcurrentConnections(4) //setEventLoopGroup的作用是建立几个线程来处理,说白了就是多线程,我这里设置的都是4,相当于16个线程同时处理。 .setEventLoopGroup(eventLoopGroup).build(); } catch (Exception e) { e.printStackTrace(); } } return apnsClient; }}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的文章
(PAT 1115) Counting Nodes in a BST (二叉查找树-统计指定层元素个数)
查看>>
(PAT 1143) Lowest Common Ancestor (二叉查找树的LCA)
查看>>
(PAT 1061) Dating (字符串处理)
查看>>
(PAT 1118) Birds in Forest (并查集)
查看>>
数据结构 拓扑排序
查看>>
(PAT 1040) Longest Symmetric String (DP-最长回文子串)
查看>>
(PAT 1145) Hashing - Average Search Time (哈希表冲突处理)
查看>>
(1129) Recommendation System 排序
查看>>
PAT1090 Highest Price in Supply Chain 树DFS
查看>>
(PAT 1096) Consecutive Factors (质因子分解)
查看>>
(PAT 1019) General Palindromic Number (进制转换)
查看>>
(PAT 1073) Scientific Notation (字符串模拟题)
查看>>
(PAT 1080) Graduate Admission (排序)
查看>>
Play on Words UVA - 10129 (欧拉路径)
查看>>
mininet+floodlight搭建sdn环境并创建简答topo
查看>>
【linux】nohup和&的作用
查看>>
【UML】《Theach yourself uml in 24hours》——hour4
查看>>
Set、WeakSet、Map以及WeakMap结构基本知识点
查看>>
【NLP学习笔记】(一)Gensim基本使用方法
查看>>
【NLP学习笔记】(二)gensim使用之Topics and Transformations
查看>>