1 什么是推送?
这个看图效果最好请直接看下图:
我们手机经常会收到如上图弹框消息,我们今天说的就是上面的弹窗信息如何推送的。一般情况我们可以通过第三的服务来给自己的app发送推送消息例如:极光推送。
极光推送
关于极光推送也是通过我们app开发同事了解到的。本来并不会跟我有任何交集,但是产品提了一个需求想通过我们自己的后台结合业务,推送自己的自定义消息弹窗。于是乎开始研究极光推送相关文档,特写此文章记录一下。
极光推送Java 后台对接有2中方式:
- 极光推送自己封装的SDK。
这种方式上手比较快速,所有api都已封装好了。有一点不好的是可能会有jar包冲突的问题。 - 通过REST API调用其服务。
这种方式更为轻便,自己的后台不用再引入极光的第三方sdk的jar包,功能上可以根据自己的需要自行进行封装。
这里我们果断选择更为轻便的REST API。实现完成后最终的效果是:我们后台将要发送的推送消息发送给极光推送,然后极光推送在发送到我们的app上。
在讲代码实现之前我们需要先看一个极光推送文档 文档地址 我们这里使用的是Push API v3版本 v3 和v2 版本区别如下:
在进行使用前我们需要一定要注意的问题:
- 调用验证
既然涉及到api调用 肯定要进行接口加密验证,极光这里是采用的是将加密的信息放入到HTTP Header(头)中来进行验证的。具体的验证方式如下:
appKey 和 masterSecret 可以在登录后 在右上角选择服务中心然后再选择开发者平台 在应用管理中的应用设置中进行查看。 - 其他需要注意问题
注意问题地址 这里我想说的只有一个,极光推送对推送的数量上并没有限制。 但是对后台调用的次数进行啦限制,免费版本的每个 Appkey 的最高推送频率为 600 次/分钟。
另外附送上客服给的回答:
好了注意事项说完了我来正式结束调用REST API 具体内容:
极光推送的内容只能是 JSON 表示的一个推送对象,在通过HttpClinet调用时要在head头中指定Content-Type: application/json 同时发送的消息是json格式的字符串内容。
josn 内容主要有以下几块:
- platform:推送平台
推送平台指的就是制定推送的是安卓还是ios 或者两者都选。 - audience:推送目标
推送目标就是推送的方式是指定的app 还是广播方式 或者是指定组的人员。 - notification:通知
通知就是具体推送消息内容信息设置。 - options:可选参数
一般用来指定是否是开发环境和离线消息保留的时间等。
具体内容以及注意事项请参考 文档 下面来说明一下我封装rest Api 后台代码介绍
1 后台封装代码说明
代码并不复杂这里就不罗列代码了具体请参考下图中的代码示例或者github上的源码。
AppPushManger: 推送服务的门面。
AppPushParam:推送服务参数类 主要是用于设置 推送标题;推送消息;推送平台等具体的推送信息。
AppPushConfig: 推送第三方服务的配置信息的抽象接口
JiGuangConfig: 推送第三方服务配置的具体实现。
IAppPush: 推送服务的接口 主要用于AppPushManger(门面)调用。
AbstractAppPush: 推送服务IAppPush的push 方法具体实现类,同时定义 认证信息获取 (getAuthorization), 参数转换(convertAppPushParam), 调用推送第三放服务(post)三个抽象方法。如需有新的推送第三放服务接入可新定义AbstractAppPush的实现类即可。
JiGuangPushImpl: 极光推送服务实现,继承 AbstractAppPush 同时实现 认证信息获取 (getAuthorization), 参数转换(convertAppPushParam), 调用推送第三放服务(post)。其中具体的配置信息实现和参数转换器的实现是在JiGuangPushImpl构造中指定。具体请参考下面JiGuangPushImpl代码示例。
IAppPushParamConverter: AppPushParam 参数转换成 第三方服务规定的转换器抽象接口
JiGuangAppPushParamConverter: 极光推送AppPushParam 参数转换成 第三方服务规定的转换器具体实现。
PlatFormEnum: 推送平台的枚举配置类。
封装UML类图如下
2 推送服务调用说明
后台服务调用AppPushManger 中的push方法,push方法中调用 IAppPush中的push方法 具体的IAppPush实现通过AppPushManger 的构造来进行传递。IAppPush的push方法具体实现是在 AbstractAppPush 进行实现的,我通过模板模式 在 AbstractAppPush 中将 认证信息获取 (getAuthorization), 参数转换(convertAppPushParam), 调用推送第三放服务(post) 定义成模板方法具体的实现在 JiGuangPushImpl中。
程序调用时序图:
注意 JiGuangPushImpl 右边的注释内容为: IAppPushParamConverter和AppPushConfig 的具体的实现是在JiGuangPushImpl 的构造方法中指定。
AbstractAppPush 具体代码如下:
package cn.zhuoqianmingyue.push.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.zhuoqianmingyue.push.config.AppPushConfig;
import cn.zhuoqianmingyue.push.convert.IAppPushParamConverter;
import cn.zhuoqianmingyue.push.impl.JiGuangPushImpl;
import cn.zhuoqianmingyue.push.param.AppPushParam;
public abstract class AbstractAppPush implements IAppPush{
private static Logger log = LoggerFactory.getLogger(JiGuangPushImpl.class);
protected IAppPushParamConverter appPushParamConverter;
protected AppPushConfig appPushConfig;
/**
* @param appPushParam
* @return
*/
public boolean push(AppPushParam appPushParam){
String pushParmJsonStr = convertAppPushParam(appPushParam);
String authorization = getAuthorization(appPushConfig);
String returnJson = post(appPushConfig,authorization,pushParmJsonStr);
if(returnJson!=null){
log.info("app push sucess:"+returnJson);
return true;
}
log.info("app push fail!");
return false;
}
/**
* 认证信息获取
* @param appPushConfig
* @return
*/
public abstract String getAuthorization(AppPushConfig appPushConfig);
/**
* 参数转换
* @param appPushParam
*/
public abstract String convertAppPushParam(AppPushParam appPushParam);
/**
* 调用推送第三放服务
* @param appPushParam
* @param authorization
* @param pushParmJsonStr
* @return
*/
public abstract String post(AppPushConfig appPushConfig, String authorization, String pushParmJsonStr);
}
JiGuangPushImpl 具体代码如下:
package cn.zhuoqianmingyue.push.impl;
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import com.arronlong.httpclientutil.HttpClientUtil;
import com.arronlong.httpclientutil.builder.HCB;
import com.arronlong.httpclientutil.common.HttpConfig;
import com.arronlong.httpclientutil.common.HttpHeader;
import com.arronlong.httpclientutil.exception.HttpProcessException;
import cn.zhuoqianmingyue.push.common.AbstractAppPush;
import cn.zhuoqianmingyue.push.config.AppPushConfig;
import cn.zhuoqianmingyue.push.config.impl.JiGuangConfig;
import cn.zhuoqianmingyue.push.convert.impl.JiGuangAppPushParamConverter;
import cn.zhuoqianmingyue.push.param.AppPushParam;
import sun.misc.BASE64Encoder;
public class JiGuangPushImpl extends AbstractAppPush{
public JiGuangPushImpl() {
super.appPushParamConverter = new JiGuangAppPushParamConverter();
super.appPushConfig = new JiGuangConfig();
}
@Override
public String getAuthorization(AppPushConfig appPushConfig) {
String appKey = appPushConfig.getAppKey();
String masterSecret = appPushConfig.getMasterSecret();
String base64_auth_string = encryptBASE64(appKey + ":" + masterSecret);
String authorization = "Basic " + base64_auth_string;
return authorization;
}
@Override
public String convertAppPushParam(AppPushParam appPushParam) {
String pushParmJsonStr = appPushParamConverter.convert(appPushParam);
return pushParmJsonStr;
}
@Override
public String post(AppPushConfig appPushConfig, String authorization, String pushParmJsonStr) {
String returnJson = null;
Header[] headers = HttpHeader.custom()
.other("Authorization", authorization.trim())
.userAgent("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36").build();
try {
HCB hcb = HCB.custom()
.timeout(1000) //超时
.pool(100, 10);//启用连接池,每个路由最大创建10个链接,总连接数限制为100个
HttpClient client = hcb.build();
HttpConfig config = HttpConfig.custom()
.headers(headers) //设置headers,不需要时则无需设置
.url(appPushConfig.getPushUrl()) //设置请求的url
.json(pushParmJsonStr) //设置请求参数,没有则无需设置
.encoding("utf-8") //设置请求和返回编码,默认就是Charset.defaultCharset()
.client(client) //如果只是简单使用,无需设置,会自动获取默认的一个client对象
.inenc("utf-8") //设置请求编码,如果请求返回一直,不需要再单独设置
.inenc("utf-8"); //设置返回编码,如果请求返回一直,不需要再单独设置
returnJson = HttpClientUtil.post(config);//post请求
} catch (HttpProcessException e) {
e.printStackTrace();
}
return returnJson;
}
/**
* BASE64加密工具s
* @param str
* @return
*/
public static String encryptBASE64(String str) {
byte[] key = str.getBytes();
BASE64Encoder base64Encoder = new BASE64Encoder();
String strs = base64Encoder.encodeBuffer(key);
return strs;
}
}
测试类具体代码如下:
package cn.zhuoqianmingyue.push.test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.arronlong.httpclientutil.exception.HttpProcessException;
import cn.zhuoqianmingyue.push.common.AppPushManger;
import cn.zhuoqianmingyue.push.enumClass.PlatFormEnum;
import cn.zhuoqianmingyue.push.impl.JiGuangPushImpl;
import cn.zhuoqianmingyue.push.param.AppPushParam;
public class JiGuangPushTest {
public static void main(String[] args) throws HttpProcessException {
AppPushParam appPushParam = new AppPushParam();
//推送弹窗的信息内容
appPushParam.setTitle("标题");
appPushParam.setMessage("测试信息");
//推动的自定义信息
Map<String, String> homeExtrasInfo = new HashMap<String,String>();
appPushParam.setExtras(homeExtrasInfo);
//设置推送的平台
List<PlatFormEnum> platformList = new ArrayList<PlatFormEnum>();
platformList.add(PlatFormEnum.ANDROID);
//platformList.add(PlatFormEnum.IOS);
appPushParam.setPlatform(platformList);
//设置推送目标 如果不设置就是广播模式
Map<String, String[]> audience = new HashMap<String,String[]>();
audience.put("registration_id", new String[] {"140fe1da9efeac6fd85"});//指定设备的registration_id进行发送
appPushParam.setAudience(audience);
//设置开发环境 极光推送只是对ios有效
appPushParam.setPushEnvironment(false);
//设置离线消息保留时长(秒)
appPushParam.setTimeToLive(60);
AppPushManger manger = new AppPushManger(new JiGuangPushImpl());
manger.push(appPushParam);
}
}
代码具体细节请参考源码: 源码地址