您当前的位置: 首页 >  Java

cuiyaonan2000

暂无认证

  • 3浏览

    0关注

    248博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Java整合APNS

cuiyaonan2000 发布时间:2021-03-02 15:09:35 ,浏览量:3

序言

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

参考资料:

  • https://blog.csdn.net/m0_37954663/article/details/106797376                          3
  • https://blog.csdn.net/qq_28483283/article/details/80514161                             2
  • https://www.jianshu.com/p/dc865924c7ee/                                                        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)

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

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

 

推送返回的Future

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

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

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

 

 

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

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

 

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

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

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

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

 

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

官方网址:https://github.com/jchambers/pushy

 

创建连接

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;

	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

关注
打赏
1638267374
查看更多评论
立即登录/注册

微信扫码登录

0.0406s