因为被这个困扰了好久,所以记录一下…
网上大部分都是关于.p12的代码,没有看到有.p8的。
先上github:https://github.com/relayrides/pushy(如果有不懂,可以再来这里看)

在Java端使用Pushy进行APNs消息推送

1 首先先加入这两个包

<!-- ios推送  pushy框架--><dependency><groupId>com.turo</groupId><artifactId>pushy</artifactId><version>0.13.7</version></dependency><dependency><groupId>io.netty</groupId><artifactId>netty-tcnative-boringssl-static</artifactId><version>2.0.20.Final</version><scope>runtime</scope></dependency>

2 身份认证
苹果APNs提供了两种认证的方式:基于JWT的身份信息token认证和基于证书的身份认证。Pushy也同样支持这两种认证方式。(待会会有区别)
如何获取苹果APNs身份认证证书可以查考官方文档。

3 Pushy使用
这是.p8的:

package com.imagedt.message.util;
import java.io.File;import com.turo.pushy.apns.auth.ApnsSigningKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.turo.pushy.apns.ApnsClient;
import com.turo.pushy.apns.ApnsClientBuilder;import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;public class APNSConnect {private static final Logger logger = LoggerFactory.getLogger(APNSConnect.class);private static ApnsClient apnsClient = null;public static ApnsClient getAPNSConnect() {if (apnsClient == null) {try {EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST).setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File("path/你的放置.p8证书的路径"),"你的teamid", "你的keyid")).setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();} catch (Exception e) {logger.error("ios get pushy apns client failed!");e.printStackTrace();}}return apnsClient;}}

还有个较好的,把文件放到resource下(建议)
路径可以写到application中。获取就是了

if (apnsClient == null) {try {EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);
//                apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)
//                        .setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File(jpushConfig.getPath()),//"C:/Users/user/Desktop/AuthKey_XNQ237XNW7.p8"
//                                jpushConfig.getTeamId(), jpushConfig.getKeyId()))//"8NM2C7228N", "XNQ237XNW7"
//                        .setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST).setSigningKey(ApnsSigningKey.loadFromInputStream(Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream(jpushConfig.getPath())),//"C:/Users/user/Desktop/AuthKey_XNQ237XNW7.p8"jpushConfig.getTeamId(), jpushConfig.getKeyId()))//"8NM2C7228N", "XNQ237XNW7".setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();} catch (Exception e) {logger.error("ios get pushy apns client failed!");e.printStackTrace();}

这是.p12证书的:

package com.imagedt.message.util;
import java.io.File;import com.turo.pushy.apns.auth.ApnsSigningKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.turo.pushy.apns.ApnsClient;
import com.turo.pushy.apns.ApnsClientBuilder;import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;public class APNSConnect {private static final Logger logger = LoggerFactory.getLogger(APNSConnect.class);private static ApnsClient apnsClient = null;public static ApnsClient getAPNSConnect() {if (apnsClient == null) {try {EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST).setClientCredentials(new File("/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();}}return apnsClient;}}

区别就是
.p8的:

.setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File(".p8路径"),"teamid", "keyid"))

.p12的:

.setClientCredentials(new File("/path/to/certificate.p12"), "p12-file-password")

7、Pushy的最佳实践
参考Pushy的官方最佳实践,我们加入了如下操作:

通过Semaphore来进行流控,防止缓存过大,内存不足;通过CountDownLatch来标记消息是否发送完成;使用AtomicLong完成匿名内部类operationComplete方法中的计数;使用Netty的Future对象进行消息推送结果的判断。

具体用法参考如下代码:

package com.imagedt.message.util;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;import com.turo.pushy.apns.util.concurrent.PushNotificationFuture;
import com.turo.pushy.apns.util.concurrent.PushNotificationResponseListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.turo.pushy.apns.ApnsClient;
import com.turo.pushy.apns.PushNotificationResponse;
import com.turo.pushy.apns.util.ApnsPayloadBuilder;
import com.turo.pushy.apns.util.SimpleApnsPushNotification;
import com.turo.pushy.apns.util.TokenUtil;import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;/*** ios消息推送*/
public class IOSPush {private static final Logger logger = LoggerFactory.getLogger(IOSPush.class);private static ApnsClient apnsClient = null;private static final Semaphore semaphore = new Semaphore(10000);//Semaphore又称信号量,是操作系统中的一个概念,在Java并发编程中,信号量控制的是线程并发的数量。/**** ios的推送* @param deviceTokens* @param alertTitle* @param alertBody* @param contentAvailable true:表示的是产品发布推送服务 false:表示的是产品测试推送服务* @param customProperty 附加参数* @param badge 如果badge小于0,则不推送这个右上角的角标,主要用于消息盒子新增或者已读时,更新此状态*/@SuppressWarnings("rawtypes")public static void push(final List<String> deviceTokens, String alertTitle, String alertBody, boolean contentAvailable, Map<String, Object> customProperty, int badge) {long startTime = System.currentTimeMillis();ApnsClient apnsClient = APNSConnect.getAPNSConnect();long total = deviceTokens.size();final CountDownLatch latch = new CountDownLatch(deviceTokens.size());//每次完成一个任务(不一定需要线程走完),latch减1,直到所有的任务都完成,就可以执行下一阶段任务,可提高性能final AtomicLong successCnt = new AtomicLong(0);//线程安全的计数器long startPushTime =  System.currentTimeMillis();for (String deviceToken : deviceTokens) {ApnsPayloadBuilder payloadBuilder = new ApnsPayloadBuilder();if (alertBody != null && alertTitle != null) {payloadBuilder.setAlertBody(alertBody);payloadBuilder.setAlertTitle(alertTitle);}//如果badge小于0,则不推送这个右上角的角标,主要用于消息盒子新增或者已读时,更新此状态if (badge > 0) {payloadBuilder.setBadgeNumber(badge);}//将所有的附加参数全部放进去if (customProperty != null) {for (Map.Entry<String, Object> map : customProperty.entrySet()) {payloadBuilder.addCustomProperty(map.getKey(), map.getValue());}}// true:表示的是产品发布推送服务 false:表示的是产品测试推送服务payloadBuilder.setContentAvailable(contentAvailable);String payload = payloadBuilder.buildWithDefaultMaximumLength();final String token = TokenUtil.sanitizeTokenString(deviceToken);SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, "com.imagedt.basho.dev", payload);try {semaphore.acquire();//从信号量中获取一个允许机会} catch (Exception e) {logger.error("ios push get semaphore failed, deviceToken:{}", deviceToken);//线程太多了,没有多余的信号量可以获取了e.printStackTrace();}final PushNotificationFuture<SimpleApnsPushNotification, PushNotificationResponse<SimpleApnsPushNotification>>sendNotificationFuture = apnsClient.sendNotification(pushNotification);//---------------------------------------------------------------------------------------------------try {final PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse = sendNotificationFuture.get();
//                System.out.println(sendNotificationFuture.isSuccess());
//                System.out.println(pushNotificationResponse.isAccepted());sendNotificationFuture.addListener(new PushNotificationResponseListener<SimpleApnsPushNotification>() {@Overridepublic void operationComplete(final PushNotificationFuture<SimpleApnsPushNotification, PushNotificationResponse<SimpleApnsPushNotification>> future) throws Exception {// When using a listener, callers should check for a failure to send a// notification by checking whether the future itself was successful// since an exception will not be thrown.if (future.isSuccess()) {final PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse =sendNotificationFuture.getNow();if (pushNotificationResponse.isAccepted()) {successCnt.incrementAndGet();} else {Date invalidTime = pushNotificationResponse.getTokenInvalidationTimestamp();logger.error("Notification rejected by the APNs gateway: " + pushNotificationResponse.getRejectionReason());if (invalidTime != null) {logger.error("\t…and the token is invalid as of " + pushNotificationResponse.getTokenInvalidationTimestamp());}}// Handle the push notification response as before from here.} else {// Something went wrong when trying to send the notification to the// APNs gateway. We can find the exception that caused the failure// by getting future.cause().future.cause().printStackTrace();}latch.countDown();semaphore.release();//释放允许,将占有的信号量归还}});//------------------------------------------------------------------/*if (pushNotificationResponse.isAccepted()) {System.out.println("Push notification accepted by APNs gateway.");} else {System.out.println("Notification rejected by the APNs gateway: " +pushNotificationResponse.getRejectionReason());if (pushNotificationResponse.getTokenInvalidationTimestamp() != null) {System.out.println("\t…and the token is invalid as of " +pushNotificationResponse.getTokenInvalidationTimestamp());}}*/} catch (final ExecutionException e) {System.err.println("Failed to send push notification.");e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}//---------------------------------------------------------------------------------------------------}//        try {
//            latch.await(20, TimeUnit.SECONDS);
//        } catch (Exception e) {
//            logger.error("ios push latch await failed!");
//            e.printStackTrace();
//        }long endPushTime = System.currentTimeMillis();logger.info("test pushMessage success. [共推送" + total + "个][成功" + (successCnt.get()) + "个],totalcost= " + (endPushTime - startTime) + ", pushCost=" + (endPushTime - startPushTime));}}

关于多线程调用client:

Pushy ApnsClient是线程安全的,可以使用多线程来调用。

最常见的两个问题:

  • 最常见的错误是两个,一个是 bad device token, 一个是Topic disallowed。这两个问题的时候,先是Topic
    Disallowed这个问题,这个是你的topic不被允许,你再和ios开发确认下应用的bundleId

  • 还有个最常见的错误是 bad deviceToken

    这个的话,你要确保你的证书是通用版证书,这个可以百度查下或者问ios开发。
    改成:ApnsClientBuilder.PRODUCTION_APNS_HOST(生产环境)
    这条命令后面是相对路径,然后名字也需要修改成你要的名字,然后要保证你的device_token是debug包还是生产包生成。如果是debug包生成的device
    token的话,就发送到https://api.development.push.apple.com/3/device/你要发送的设备的device_token这个苹果的沙盒地址,如果你的device
    token是生产包的话,需要发送到https://api.push.apple.com/3/device/你要发送的设备的device_token这个苹果的生产环境地址。

APNs苹果推送使用的是pushy框架+.p8证书(java后台)(附上与.p12的不同)相关推荐

  1. 小米华为苹果推送相关Nginx转发推送配置

    小米华为苹果推送相关Nginx转发推送配置## 标题 1.1推送类型及涉及的推送url 目前涉及到的推送类型包括: 1).APNS苹果推送 2).华为厂商推送 3).小米厂商推送 4).魅族厂商推送 ...

  2. 苹果推送通知服务(APNs)编程(转)详细步骤

    iPhone 对于应用程序在后台运行有诸多限制(除非你越狱).因此,当用户切换到其他程序后,原先的程序无法保持运行状态.对于那些需要保持持续连接状态的应用程序(比如社区网络应用),将不能收到实时的信息 ...

  3. 苹果推送服务(APNs)中文文档

    工作中需要用到APNs服务,所以打算将APNs的官方文档翻译出来,供有需求的朋友一起参考,水平有限,难免出错,还望不吝赐教. 总览 本地通知和远程通知 本地通知和远程通知就是所谓的两种不同类型的通知, ...

  4. 苹果推送消息服务(转)

    苹果推送消息服务 .NET 开源项目:https://github.com/Redth/APNS-Sharp Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个 ...

  5. 苹果推送通知办事教程 Apple Pushnb

    原文地址:(转)苹果推送通知办事教程 Apple Push Notification Services Tutorial 作者:浪友dans 苹果推送通知办事教程 Apple Push Notific ...

  6. Apple Push Notification Service(苹果推送服务)

    https://developer.apple.com/library/IOS/documentation/NetworkingInternet/Conceptual/RemoteNotificati ...

  7. android与苹果 推送,科普下安卓和苹果的推送区别

    看到很多人说苹果推送比安卓好太多,安卓关掉app就没法收到推送消息,作为从业者,来给大家科普下安卓和苹果的推送区别. 首先上苹果推送的整体流程图 简单描述一下,开发者先接入苹果推送服务,有消息需要推送 ...

  8. 检测苹果推送证书有效性

    写apple 推送程序,经常遇到发送失败或接收不到的问题. 以下收集了相关问题 转至http://www.haodaima.net/art/2420853 使用APNS 搭建苹果推送服务器错误:una ...

  9. 苹果推送iOS13.1.3更新:iOS13发布仅一个月疯狂补Bug

    10月16日,苹果推送了iOS 13.1 .3系统升级,主要包括针对iPad和iPhone的错误修复和改进,而这也是iOS 13发布仅一个月以来推送的第四个升级补丁. 具体改进如下: 解决了使用Gam ...

最新文章

  1. 深入Mysql,SqlServer,Oracle主键自动增长的设置详解
  2. [轉]俞老师在同济大学的演讲词:度过有意义的生命
  3. 电脑打字学习_VOL.3,NO.2 | 小学一年级,爸爸管学习,完全就是个笑话!
  4. 机器学习003-Kernel
  5. springboot在intellij中的热部署(Hot swapping)配置
  6. Flex通过Blazeds利用Remoteservice与后台java消息推送
  7. linux版本FTP下载
  8. Docker-构建/启停容器镜像及常用命令介绍
  9. HTML标记特定,制作asp:标记特定的HTML标记
  10. UE4 中文文档阅读
  11. 百度的“知心搜索”揭秘
  12. 如何在html中使用 es6语法让浏览器识别
  13. 淘宝帝国是如何创建的连载04
  14. n维椭球体积公式_考前必背!数量关系、资料分析常用公式汇总!不再头大!...
  15. 开源OA办公平台教程:手机APP指纹认证的配置
  16. matlab 二维矩形函数,科学网—利用MATLAB对非矩形域实现二维插值 - 张乐乐的博文...
  17. JS入门笔记九:循环精灵图案例
  18. 触摸板失效,fn快捷键不显示,神舟control center3.0无法打开等问题重装驱动
  19. 基于对抗生成网络的图像转换技术【论文笔记】
  20. 12 | 理解电路:从电报机到门电路,我们如何做到“千里传信”?

热门文章

  1. React Native Expo 项目中添加动画音效
  2. 西泽金融: 银行业的下半场
  3. 论文阅读+实战:SimGNN:A Neural Network Approach to Fast Graph Similarity Computation
  4. 扡扫机器人_米家扫拖机器人:扫地拖地一把抓,小爱同学不落下
  5. 视频教程-Android之ViewPager,Fragment知识全讲-Android
  6. 博客推广及搜索引擎优化
  7. kali Linux渗透测试黑客专用系统命令
  8. Java跨年祝福语代码_跨年祝福语大全
  9. Matlab常见错误---带有下标的赋值维度不匹配。
  10. 我的Java开发技术总结