Android集成Huawei PUSH(五)——服务端开发
一、服务器获取Access Token
开发者在开发服务器端时,首先要调用请求Access Token接口获取接入Token,Access Token是应用服务器实现发送PUSH 消息功能的必须条件。
Access Token存在有效期,当前版本Access Token的有效期为1个小时,有效期通过响应参数消息的expires_in参数传达。Access Token在有效期内请尽量复用,如果开发者应用服务器频繁的申请Access Token,可能会被流控。Access Token过期前,业务要根据这个有效时间提前去申请新Access Token。
服务端获取Access Token的主要代码如下:
public class PushNcMsg
{
private static String appSecret = "appSecret";//用户在华为开发者联盟申请Push服务获取的服务参数
private static String appId = "12345678";//用户在华为开发者联盟申请Push服务获取的服务参数
private static String tokenUrl = "https://login.cloud.huawei.com/oauth2/v2/token"; //获取认证Token的URL
private static String apiUrl = "https://api.push.hicloud.com/pushsend.do"; //应用级消息下发API
private static String accessToken;//下发通知消息的认证Token
private static long tokenExpiredTime; //accessToken的过期时间
//获取Access Token
private static void refreshToken() throws IOException
{
String msgBody = MessageFormat.format(
"grant_type=client_credentials&client_secret={0}&client_id={1}",
URLEncoder.encode(appSecret, "UTF-8"), appId);
String response = httpPost(tokenUrl, msgBody, 5000, 5000);
JSONObject obj = JSONObject.parseObject(response);
accessToken = obj.getString("access_token");
tokenExpiredTime = System.currentTimeMillis() + (obj.getLong("expires_in") - 5*60)*1000;
}
}
其中:
①appId 和appSecret 为开发者在开发者联盟为应用申请Huawei PUSH 服务时的参数;
②tokenUrl为获取认证Token的URL,参数的值是一个固定值,开发时直接复制即可
③apiUrl为应用级消息下发API的URL,参数的值是一个固定值,开发时直接复制即可
-----------------------------------------------------------------------------------------
二、服务端发送PUSH消息
具体步骤如下:
1.发送PUSH消息前判断AT是否过期,如果过期需要重新申请。
其中refreshToken()为上一章节实现的获取AT的方法。
示例代码:
if (tokenExpiredTime <= System.currentTimeMillis()) {
refreshToken();
}
2.封装PUSH Token
USH Token为客户端调用getToken()接口获取到保存到服务器的,开发者需要读取服务器上的token,将其放到JSONArray中。服务器接口每次只支持发送100个,如果想使用更大发送量,请参考如下链接:
https://developer.huawei.com/consumer/cn/help/60103
示例代码:
JSONArray deviceTokens = new JSONArray();//目标设备Token
deviceTokens.add("12345678901234561234567890123456");
deviceTokens.add("22345678901234561234567890123456");
deviceTokens.add("32345678901234561234567890123456");
设备的Token值在客户端获取之后可以通过网络请求调用后台接口获得,或者保存至数据库中,服务端通过查询数据库获得。
3.封装推送消息body,用于显示通知栏消息显示的标题和内容
示例代码:
JSONObject body = new JSONObject();//仅通知栏消息需要设置标题和内容,透传消息key和value为用户自定义
body.put("title", "Push message title");//消息标题
body.put("content", "Push message content");//消息内容体
4.封装消息点击动作的参数
示例代码:
JSONObject param = new JSONObject();
param.put("appPkgName", "com.huawei.hms.hmsdemo");//定义需要打开的appPkgName,根据实际情况修改
5.封装消息点击动作
用于定义通知栏点击行为,param为步骤4封装的点击动作参数
示例代码:
JSONObject action = new JSONObject();
action.put("type", 3);//类型3为打开APP,其他行为请参考接口文档设置
action.put("param", param);//消息点击动作参数
说明:type=1表示自定义Intent,type=2表示打开URL,type=3表示打开APP。
6. 封装消息类型,用于定义消息类型
区分是通知栏消息还是透传消息,其中action和body分别是步骤5和步骤3封装的消息
示例代码:
JSONObject msg = new JSONObject();
msg.put("type", 3);//3: 通知栏消息,异步透传消息请根据接口文档设置
msg.put("action", action);//消息点击动作
msg.put("body", body);//通知栏消息body内容示例代码
说明:type=1表示透传消息,type=3表示通知栏消息,type=2和4为预留。
7. 封装扩展消息
扩展消息中可以设置biTag用于消息打点,也可以携带customize参数用于触发通知栏点击事件的onEvent回调
示例代码:
JSONObject ext = new JSONObject();//扩展信息,含BI消息统计,特定展示风格,消息折叠。
ext.put("biTag", "Trump");//设置消息标签,如果带了这个标签,会在回执中推送给CP用于检测某种类型消息的到达率和状态
JSONObject temp= new JSONObject();
temp.put("season","Spring");
temp.put("weather","raining");
JSONArray customize = new JSONArray();
customize.add(temp);
ext.put("customize",customize);
8. 封装整个消息体,可自定义实现单击消息后的跳转动作
扩展消息中可以设置biTag用于消息打点,也可以携带customize参数用于触发通知栏点击事件的onEvent回调
示例代码:
JSONObject hps = new JSONObject();//华为PUSH消息总结构体
hps.put("msg", msg);
hps.put("ext", ext);
JSONObject payload = new JSONObject();
payload.put("hps", hps);
9. 定义httpPost()方法,用于消息发送
示例代码:
public static String httpPost(String httpUrl, String data, int connectTimeout, int readTimeout) throws IOException
{
OutputStream outPut = null;
HttpURLConnection urlConnection = null;
InputStream in = null;
try
{
URL url = new URL(httpUrl);
urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
urlConnection.setConnectTimeout(connectTimeout);
urlConnection.setReadTimeout(readTimeout);
urlConnection.connect();
// POST data
outPut = urlConnection.getOutputStream();
outPut.write(data.getBytes("UTF-8"));
outPut.flush();
// read response
if (urlConnection.getResponseCode() < 400)
{
in = urlConnection.getInputStream();
}
else
{
in = urlConnection.getErrorStream();
}
List<String> lines = IOUtils.readLines(in, urlConnection.getContentEncoding());
StringBuffer strBuf = new StringBuffer();
for (String line : lines)
{
strBuf.append(line);
}
System.out.println(strBuf.toString());
return strBuf.toString();
}
finally
{
IOUtils.closeQuietly(outPut);
IOUtils.closeQuietly(in);
if (urlConnection != null)
{
urlConnection.disconnect();
}
}
}
10. 封装整个http消息并发送
示例代码:
String postBody = MessageFormat.format(
"access_token={0}&nsp_svc={1}&nsp_ts={2}&device_token_list={3}&payload={4}",
URLEncoder.encode(accessToken,"UTF-8"),
URLEncoder.encode("openpush.message.api.send","UTF-8"),
URLEncoder.encode(String.valueOf(System.currentTimeMillis() / 1000),"UTF-8"),
URLEncoder.encode(deviceTokens.toString(),"UTF-8"),
URLEncoder.encode(payload.toString(),"UTF-8"));
String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + appId + "\"}", "UTF-8");
httpPost(postUrl, postBody, 5000, 5000); //发送PUSH消息
-----------------------------------------------------------------------------------------
三、实例代码
private static String appID = "xxxxxxxx"; //用户在华为开发者联盟申请Push服务获取的APPID
private static String appSecret ="xxxxxxxx"; //用户在华为开发者联盟申请Push服务获取的APPSECRET
private static String tokenUrl = "https://login.cloud.huawei.com/oauth2/v2/token"; //获取认证Token的URL
private static String apiUrl = "https://api.push.hicloud.com/pushsend.do"; //应用级消息下发API
private static String accessToken;//下发通知消息的认证Token
private static long tokenExpiredTime; //accessToken的过期时间
//获取Access Token
private void refreshToken() throws Exception {
String msgBody = MessageFormat.format("grant_type=client_credentials&client_secret={0}&client_id={1}",
URLEncoder.encode(appSecret, "UTF-8"), appID); //拼接信息
String response = httpPost(tokenUrl,msgBody,5000,5000);
JSONObject object = JSONObject.parseObject(response);
accessToken = object.getString("access_token");
tokenExpiredTime = System.currentTimeMillis() + (object.getLong("expires_in") - 5 * 60) * 1000;
}
private void sendPushMessage() throws Exception{
try{
JSONArray deviceTokens = new JSONArray();//目标设备Token
deviceTokens.add("12345678901234561234567890123456");
//封装推送消息Body用于显示通知栏消息显示的标题和内容
JSONObject bodyObject = new JSONObject(); //仅通知栏消息需要设置标题和内容,透传消息key和value为用户自定义
bodyObject.put("title", "haha"); //消息标题
bodyObject.put("content", "test"); //消息内容体
//封装消息点击动作参数
JSONObject paramObject = new JSONObject();
paramObject.put("appPkgName", "com.xxx.xxx");
//封装消息点击动作用于定义通知栏点击行为
JSONObject actionObject = new JSONObject();
actionObject.put("type", 1); //type=1表示自定义intent
//封装intent信息
JSONObject intentObject = new JSONObject();
intentObject.put("intent", "intent://com.huawei.pushtest/notify_detail#Intent;scheme=customscheme;launchFlags=0x10000000;S.package=test;end");
actionObject.put("param", intentObject);
//封装消息类型用于定义消息类型,区分是通知栏消息还是透传消息
JSONObject msgObject = new JSONObject();
msgObject.put("type", 3); //type=3表示通知栏消息
msgObject.put("action", actionObject); //消息点击动作
msgObject.put("body", bodyObject); //通知栏消息body内容
//封装扩展消息
JSONObject extObject = new JSONObject();
extObject.put("biTag", "Trump"); //设置消息标签,如果带了这个标签,会在回执中推送给CP用于检测某种类型消息的到达率和状态
//封装整个消息体
JSONObject huaweiPushObject = new JSONObject(); //华为PUSH消息总结构体
huaweiPushObject.put("msg", msgObject);
huaweiPushObject.put("ext", extObject);
JSONObject payLoadObject = new JSONObject();
payLoadObject.put("hps", huaweiPushObject);
//封装整个HTTP消息并发送
String postBody = MessageFormat.format(
"access_token={0}&nsp_svc={1}&nsp_ts={2}&device_token_list={3}&payload={4}",
URLEncoder.encode(accessToken,"UTF-8"),
URLEncoder.encode("openpush.message.api.send","UTF-8"),
URLEncoder.encode(String.valueOf(System.currentTimeMillis() / 1000),"UTF-8"),
URLEncoder.encode(deviceTokens.toString(),"UTF-8"),
URLEncoder.encode(payLoadObject.toString(),"UTF-8"));
String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + appID + "\"}", "UTF-8");
httpPost(postUrl, postBody, 5000, 5000);
}catch(Exception e){
e.printStackTrace();
}
}
public String httpPost(String httpUrl, String data, int connectTimeOut, int readTimeOut) throws Exception{
OutputStream outputStream = null;
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try{
URL url = new URL(httpUrl);
urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
urlConnection.setConnectTimeout(connectTimeOut);
urlConnection.setReadTimeout(readTimeOut);
urlConnection.connect();
// POST data
outputStream = urlConnection.getOutputStream();
outputStream.write(data.getBytes("UTF-8"));
outputStream.flush();
// read response
if (urlConnection.getResponseCode() < 400){
inputStream = urlConnection.getInputStream();
}else{
inputStream = urlConnection.getErrorStream();
}
List<String> lines = IOUtils.readLines(inputStream, urlConnection.getContentEncoding());
StringBuffer strBuf = new StringBuffer();
for (String line : lines){
strBuf.append(line);
}
return strBuf.toString();
}finally{
IOUtils.closeQuietly(outputStream);
IOUtils.closeQuietly(inputStream);
if (urlConnection != null){
urlConnection.disconnect();
}
}
}
-----------------------------------------------------------------------------------------
四、附录
开发发送PUSH接口时可参照API:https://developer.huawei.com/consumer/cn/service/hms/catalog/huaweipush_agent.html?page=hmssdk_huaweipush_api_reference_agent_s2
接口回执API:https://developer.huawei.com/consumer/cn/service/hms/catalog/huaweipush_agent.html?page=hmssdk_huaweipush_api_reference_agent_s3