技术标签: 第三方插件 APNS IOS 框架 demo pushy
最近公司需要做IOS消息推送的功能,我负责后台推送,IOS端数据处理以及回调我不负责,本篇文章主要介绍基于java的apns消息推送,使用框架为pushy。
声明:我先前也没有接触过这个IOS推送,自己研究了两天,通过百度,对比各个框架的优缺点,最后选择了这个框架,有说的不对的地方,还希望指出来,当然现在很多公司都是采用第三方平台的方式来推送,这里不介绍。
目前比较流行的框架在github上可以直接看到,通过star的多少,可以判断其流行程度,原本准备使用notnoop框架,但是考虑到其基于http1框架,没有返回值,而且框架比较老,很久没有维护了,所以没用。
Pushy是用于发送APN(iOS,MacOS和Safari)推送通知的Java库。它由Turo的工程师编写和维护。
Pushy使用Apple的基于HTTP / 2的APN协议发送推送通知,并支持TLS和基于令牌的身份验证。它与其他推送通知库区别开来,重点在于全面的文档记录,异步操作和用于工业规模操作的设计; 通过Pushy,维护到APN网关的多个并行连接可以轻松高效地向大量不同的应用程序(“主题”)发送大量通知。
我们相信Pushy已经是从Java应用程序发送APN推送通知的最佳工具,并且我们希望您能通过错误报告和拉取请求帮助我们更好地实现它。如果您对使用Pushy有任何疑问,请加入我们的Pushy邮件列表或查看wiki。谢谢!
上面摘抄于git文档
留一个网址,具体源码大家自己去看吧https://github.com/relayrides/pushy
我用的maven项目,需要先加入jar
<dependency>
<groupId>com.turo</groupId>
<artifactId>pushy</artifactId>
<version>0.13.1</version>
</dependency>
直接先上代码
package com.huali.excutemq.util;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
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;
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并发编程中,信号量控制的是线程并发的数量。
@SuppressWarnings("rawtypes")
public void push(final String deviceToken, 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());
}
}
payloadBuilder.setContentAvailable(contentAvailable);
String payload = payloadBuilder.buildWithDefaultMaximumLength();
final String token = TokenUtil.sanitizeTokenString(deviceToken);
SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, "com.Huali-tec.HLSmartWay", payload);
// try {
// semaphore.acquire();//从信号量中获取一个允许机会
// } catch (Exception e) {
// logger.error("ios push get semaphore failed, deviceToken:{}", deviceToken);//线程太多了,没有多余的信号量可以获取了
// e.printStackTrace();
// }
final Future<PushNotificationResponse<SimpleApnsPushNotification>> future = apnsClient.sendNotification(pushNotification);
future.addListener(new GenericFutureListener<Future<PushNotificationResponse>>() {
@Override
public void operationComplete(Future<PushNotificationResponse> pushNotificationResponseFuture) throws Exception {
if (future.isSuccess()) {
final PushNotificationResponse<SimpleApnsPushNotification> 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 (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));
}
}
苹果消息推送大致流程:
1、首先通过上面这段代码,将消息推送到苹果服务器(苹果公司买了几十万台服务器专门用于做这个事情),然后苹果服务器收到消息以后,根据devicetoken将对应的内容推送到对应的设备(ps:而安卓手机就是在系统后台有一个监听程序,一直在耗电,这也是为什么苹果手机比较省电的原因,而安卓手机比较费电,你下载的app越多,这个差异就越明显)
。
2、先前在网上看资料说如果你推送的devicetoken有错误,链接就会中断或者暂停一段时间,这样就会造成延迟,目前我使用这个框架测试十万条数据,暂时没有发现这个情况。
3、由于本案例使用的是消息队列加多个消费者推送消息,所以代码中每次只推送了一条消息,注释部分打开,devicetoken可以存放多个用户设备标志用于多次推送,这里用了多个消费者。
4、此段代码封装了一个方法getAPNSConnect(),代码如下
package com.huali.excutemq.util;
import java.io.File;
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("C:\\Users\\Administrator\\Desktop\\PushCertificate-HualiSmartWay.p12"), "#edcvfr4%t")
.setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();
} catch (Exception e) {
logger.error("ios get pushy apns client failed!");
e.printStackTrace();
}
}
return apnsClient;
}
}
这个方法用于服务器与苹果服务器的链接,为什么要单独写出来呢?主要是总不能每消费一个devicetoken就重新链接一次苹果服务器吧,每次执行推送消息程序的时候,会去检查一下这个链接是否还在(与苹果服务器的连接长时间没有数据发过去的话,苹果服务器会将它主动断掉),如果还在的话,就直接用了。
5、这里主要介绍一下setConcurrentConnections和setEventLoopGroup
setConcurrentConnections用于设置服务器与苹果服务器建立几个链接通道,这里是建立了四个,链接通道并不是越多越好的,具体速度自己百度
setEventLoopGroup的作用是建立几个线程来处理,说白了就是多线程,我这里设置的都是4,相当于16个线程同时处理。
6、关于推送速度,我没有测试这种4乘以4的速度,我测试了十万条数据,分别是2、10、50消费者的时候的消费速度,2个消费者速度大概600个推送每秒,10个消费者3000个推送每秒。50个消费者6000个推送每秒,大致上跟网上说的速度差不多。
7、上面代码中涉及到一些多线程的知识,我在后面有简单的标注,具体的还请大家自己再网上学习。
8、关于苹果服务器返回的消息(返回消息是否推送成功),第一个返回的消息是Future<PushNotificationResponse<SimpleApnsPushNotification>> future
里面包含服务器发送消息到苹果服务器是否成功。这个成功的话还需要知道苹果服务器是否将消息成功推送到用户的手机上,这个是final PushNotificationResponse<SimpleApnsPushNotification> response = future.getNow();
这里获取的response是是否将消息成功推送到用户手机上,这里是一个监听,即在服务器将消息发送以后,就设置了一个监听,用来监听苹果服务器是否将消息成功推送到用户手机上,当然,监听不会等到消息返回以后才继续往下执行代码,而是直接往下执行代码。所以这里有一个代码latch.await(20, TimeUnit.SECONDS);
这句代码的意思就是等待当前线程执行完毕(即有返回消息以后),再继续往下执行,目的是为了统计消息发送成功的数量,当然我这里是一个个执行的,所以就没有必要去统计了,不然会有延迟的。
额,顺便贴上一张图:
这个图片是消息消息推送的主要参数。
这里说一下,苹果推送分为静默推送(contentAvailable值为0,app端没有回调函数)和非静态推送(contentAvailable值为1,app端有回调函数,比如 对数据进行一些处理什么的。),如果你设置title和body为null,则app不会推送(就是下拉没有数据,这个情况一般用来更新badge数量)
2、还有一点顺带提一下:payloadBuilder.addCustomProperty(map.getKey(),map.getValue());
这句代码是后台服务器传给app的附加参数,这个需要根据具体需求,具体分析。
对了还有一个证书的问题:自己去苹果官网申请,只需要一个.p12格式的证书,以及证书的密码就可以了。具体申请步骤请自己百度
上面大概就是我知道的所有有关apns的知识,有什么问题可以评论,能回答的我尽量回答
&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;meta charset="UTF-8"&gt;&lt;title&gt;树形菜单&lt;/title&gt;&lt;script type="text/javascript" src="../js/jquery-1.8.3.js"&am
1.在任意一个路径下,新建一个文件夹,把想要修改的文件复制拷贝到该文件夹下2.在开始菜单搜索框输入cmd,打开命令行,输入"cd f:\pictures"然后回车,进入该文件夹,然后再输入命令"dir /b>rename.xls",随后会创建一个名为rename的xls文件3.打开此xls文件,在B1列输入你想要修改之后的文件名,比如"1.jpg",鼠标移到B1右下角+号进行向下拖动,将所有B列都添加文件名3.鼠标选中C1单元格,并在编辑框中输入公式="ren "&A1&
sed默认的匹配方式是基本正则表达式,用基本正则表达式匹配时. * ^ $ [ ] \这些字符默认为表示元字符含义,若要表示原来字面上的意思就必须转义,其它字符如,( ) + ? | { }在基本正则表达式中表示字面上的意思不需要转义,但表示元字符的意思时需要转义。而在sed使用扩展正则表达式时除了. * ^ $ [ ] \是表示元字符含义,,( ) + ? | { }也是表示元字符含义,若要表示...
在C/C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量存储区。里面的变量通常是局部变量、函数参数等。 堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统...
TIOBE 公布了 2020 年 12 月的编程语言排行榜。TIOBE 将在下个月公布 2020 年的年度编程语言,一年内排名率增长最高的编程语言将获得这一称号。目前,Python 以 +1.90%数据遥遥领先。其次分别是 C++(+0.71%)、R(+0.60%)和 Groovy(+0.69%)。TIOBE CEOPaul Jansen 称,在今年的最后一个月,Python被反超的可能性非常低。这意味着 Python 很可能第四次夺得冠军,这在 TIOBE 指数的历史上也是一个新记...
c语言捕获kill信号Programs may want to catch the kill signals sent by the kill command in C programs on Linux so that it can gracefully shutdown itself before it is killed. For example, I have a daemon progd...
wxml:<input placeholder-class="placeholderinput" placeholder="请选择企业" ></input>wxss.placeholderinput { font-size: 30rpx; font-family: Microsoft YaHei; font-weight: 400; ...
# github.com/gin-gonic/gin/bindingsrc\github.com\gin-gonic\gin\binding\default_validator.go:48:14: undefined: validator.Configsrc\github.com\gin-gonic\gin\binding\default_validator.go:49:29: too man...
常用的加解密算法的优缺点、应用场景总结网址:https://www.jianshu.com/p/ea229b329347?utm_campaign一、加解密的基础知识1、对称密钥加密对称密钥加密(一个密钥),也叫做共享密钥加密或机密密钥加密,使用发件人和收件人共同拥有的单个密钥。这种密钥既用于加密,也用于解密,叫做机密密钥。对称密钥加密是加密大量数据的一种行之有效的方法...
贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。在Flash4中还没有
<!-- @page { size: 21cm 29.7cm; margin: 2cm } P { margin-bottom: 0.21cm } --> 今天装了一下BT下载软件,在ubuntu论坛里面提到的Azureus不是太好用,老是连接错误,所以就到网上找了另一个BT下载软件bitstormlite。
发信人: Stony (My Digital Stony·喜欢上海的理由), 信区: Borland标 题: 如何使DELPHI编译出来的EXE文件最小[zz]发信站: 饮水思源 (2004年08月11日11:49:38 星期三), 站内信件如何使DELPHI编译出来的EXE文件最小 作者: 出处: 更新时间: 2004年08月04日 1、尽量用API函数来写程序。一个完全用API写的