在上一个FCM推送发布上线后,我开始了下一个开发任务。这次需要我接入华为服务器实现消息推送,其实接入华为要比接入fcm容易很多,因为华为的内部代码构建结构较为明了,可以很好地让开发者进行阅读和理解,从而很好地进行二次开发。话不多说,我写了一个简单的demo,将华为推送进行一个简单的介绍。
目录
1.官方API阅读
2.通知消息和透传消息的发送方式
(1)通知消息
(2)透传消息
3.如何进行access_token刷新
4.总结
华为的官方文档要比谷歌的文档更容易理解,也更为直接,官方地址如下
https://developer.huawei.com/consumer/cn/doc/development/HMS-References/push-sendapi
从文档中可以看出,每次我们可以发送最多1000个token,也就是1000个设备。如果应用超过1000个,那么就需要分批次发送。
如果大家直接从华为的官网下载代码进行阅读,会发现内部的实现代码是在第一次发起请求时启动一个定时任务,这个定时任务是用来更新每次请求发送时华为服务器要求携带的acess_token的,但是我们不建议这样做。作为开发者,我们应该可以自主去更新一个app对应的acess_token,具体的方法我会在下一部分的内容中说明如何请求acess_token。
在请求到access_token之后,下一步就是按照官方的要求发送请求。首先是在请求头中拼接如下的信息,tokenType是我们在请求access_token时返回的字段,一般默认都是Bear。accessToken则是我们请求到的授权码。
httpPost.setHeader("Authorization", tokenType+" "+ accessToken);
httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
在发送完毕后,我们可以根据请求的状态码判断我们的请求是否成功发送到了华为服务器,注意这里是指请求是否到达了华为的服务器,而不是华为向我们回执消息正确收到。
CloseableHttpResponse response = httpClient.execute(httpPost);
String rpsContent = EntityUtils.toString(response.getEntity());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
JSONObject jsonObject = JSONObject.parseObject(rpsContent);
String code = jsonObject.getString("code");
String msg = jsonObject.getString("msg");
String requestId = jsonObject.getString("requestId");
return SendResponse.fromCode(code, msg, requestId);
}
可以看到的statusCode为200时则为请求发送成功,具体的信息在返回的
SendResponse.fromCode(code, msg, requestId); 对象中,具体这个对象中包含的信息可以在
进行对比。
消息的发送分为通知消息和透传消息两种。通知消息是由华为转发到设备,由系统处理进行弹出消息提示,用户在点击消息后根据intent字段,这个intent字段是安卓开发端规定的触发事件的协议,根据这个协议按照华为的开发模板进行一定的响应。
透传消息和通知消息,这个消息传递的过程用户不会感知,这个消息会由华为直接传递给安卓客户端,安卓客户端进行解析后透过调用app内部声明的方法进行响应。
对我们后端开发而言,这些我们并不关心,我们关心的是按照安卓端的要求正确发送通知消息或者透传消息。所以这部分内容我打算说明一下如何发送两种消息
通知消息可以携带很多的信息,官方链接对这些字段说明非常详细,而且样式也很多,我只把说明最关键的字段,这些字段是不可缺少的。
发送请求主要封装在三个类中,分别为
Notification ,
AndroidNotification
以及
AndroidConfig
前面两个类,可以看出都和Notification有关系,第一个类Notification是通用的,通常设置的是title和body,也就是标题和内容。
第二个AndroidNotification就是和安卓系统有关系的一些设置,在我编写的demo中,它主要设置了如下属性。
AndroidNotification.Builder builder = AndroidNotification.builder();
//给builder赋值
builder
.setClickAction(ClickAction.builder().setType(1).setIntent(intent).build())
.setStyle(1)
.setBigTitle(bigTitle)
.setBigBody(bigBody)
.build();
//构建一个androidNotification
AndroidNotification androidNotification = builder.build();
首先是clickAction,这个设定的是点击消息后进行响应的跳转操作。clickAction的说明如下所示
https://developer.huawei.com/consumer/cn/doc/development/HMS-References/push-sendapi#clickaction
在我的项目中,因为是安卓自定义设置跳转动作,所以我将type设置为1,intent则为安卓声明好的协议字段。
至于这个style,则是这边需求为做大文本推送,特意设置为1。如果这个地方设置为1,那么必须要设置bigTitle和bigBody的
接下来是AndroidConfig的设置
AndroidConfig.Builder androidConfigBuilder = AndroidConfig.builder();
androidConfigBuilder
//对所有离线消息都缓存
.setCollapseKey(-1)
.setUrgency(Urgency.NORMAL.getValue())
//消息缓存时间,单位是秒
.setTtl(String.valueOf(ttl) )
.setNotification(androidNotification)
.build();
if(biTag!=null){
androidConfigBuilder.setBiTag(biTag);
}
AndroidConfig androidConfig = androidConfigBuilder.build();
这些字段的解释官网也是有的
其中bi_tag字段是我会根据业务的需求选择是否生成bi_tag进行通知消息的下发情况。
透传消息的概念我前面已经说过,那么我主要讲一下怎么发送透传消息。
可以看到如果要发送data字段,那么之前的三个类是不需要我们创建先前的三个对象的,相比起来透传数据的发送方式非常简单。但是记住,发送透传数据时一定不能携带上述的三个属性,否则会报错。
最后一部分,主要讲述一下如何进行access_token的刷新。其实原理非常简单,只需要提供appId和appSecret。这个安卓开发一定知道,这是app授权下发的消息。代码如下:
public static JSONObject refreshToken(String clientId, String appSecret, String tokenServerUrl) throws Exception {
String requestBody = createRequestBody(clientId, appSecret);
HttpPost httpPost = new HttpPost(tokenServerUrl);
StringEntity entity = new StringEntity(requestBody);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
httpPost.setEntity(entity);
CloseableHttpResponse response = httpClient.execute(httpPost);
String jsonStr = EntityUtils.toString(response.getEntity());
int statusCode = response.getStatusLine().getStatusCode();
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
if (statusCode == 200) {
//在这里完成你的业务逻辑
}else {
System.out.println(jsonObject.getString("error_description"));
}
return jsonObject;
}
其实推送的原理都是十分相似的,都是通过获取一个access_token然后发送信息请求,在不断的业务代码编写中,我也在不断改善自己的代码风格,让自己的模块的耦合性降低,从而实现更好的代码扩展。