極光訊息推送伺服器端開發實現推送(上)
阿新 • • 發佈:2019-01-23
以前一直使用的極光的手動輸入推送內容然後推送到客戶端,今天遇到了推送頻率比較高且比較有規律的內容,比如事實天氣。這樣就需要用我們自己的伺服器來自動生成推送內容了。
可以看到,上面兩句話很醒目,我們看看它封裝的REST API是個什麼東西,再點進去看看
上面兩句話讀了一下,看來我們的運氣還不錯,這個應該就是了。
進去看了半天,大概明白意思了,已經幫助我們封裝好了,現在我們只需要下載jar包和它提供的例項文件。
進去後發現又對RESI API進行了更詳細的說明,比如引數、頻率限制等。
好吧,我們看看推送訊息和通知,如下圖點選進入
進去之後就看到了真真需要的api介面了,這裡有各個方法和引數的說明。如果有的朋友還覺得看不懂,簡單,直接下載示例程式碼(抄襲誰不會啊
我下載官方示例程式碼,新建立了一個專案如下:
訊息傳送端程式碼:
package com.meritit.tuisong.service; import java.util.HashMap; import java.util.Map; import cn.jpush.api.ErrorCodeEnum; import cn.jpush.api.IOSExtra; import cn.jpush.api.JPushClient; import cn.jpush.api.MessageResult; public class JPushClientExample { private static final String appKey ="5d30bebd28335593a13454861"; //必填,例如466f7032ac604e02fb7bda89 private static final String masterSecret = "0e0cc80c6f6a4703bec9ed191";//"13ac09b17715bd117163d8a1";//必填,每個應用都對應一個masterSecret private static JPushClient jpush = null; /** * 儲存離線的時長。秒為單位。最多支援10天(864000秒)。 * 0 表示該訊息不儲存離線。即:使用者線上馬上發出,當前不線上使用者將不會收到此訊息。 * 此引數不設定則表示預設,預設為儲存1天的離線訊息(86400秒)。 */ private static long timeToLive = 60 * 60 * 24; public static void main(String[] args) { /* * Example1: 初始化,預設傳送給android和ios,同時設定離線訊息存活時間 * jpush = new JPushClient(masterSecret, appKey, timeToLive); */ /* * Example2: 只發送給android * jpush = new JPushClient(masterSecret, appKey, DeviceEnum.Android); */ /* * Example3: 只發送給IOS * jpush = new JPushClient(masterSecret, appKey, DeviceEnum.IOS); */ /* * Example4: 只發送給android,同時設定離線訊息存活時間 * jpush = new JPushClient(masterSecret, appKey, timeToLive, DeviceEnum.Android); */ jpush = new JPushClient(masterSecret, appKey, timeToLive); /* * 是否啟用ssl安全連線, 可選 * 引數:啟用true, 禁用false,預設為非ssl連線 */ //jpush.setEnableSSL(true); //測試傳送訊息或者通知 testSend(); } private static void testSend() { // 在實際業務中,建議 sendNo 是一個你自己的業務可以處理的一個自增數字。 // 除非需要覆蓋,請確保不要重複使用。詳情請參考 API 文件相關說明。 int sendNo = getRandomSendNo(); String msgTitle = "+;//jpush\"\""; String msgContent = "\\&;w\"\"a--【\npush】"; /* * IOS裝置擴充套件引數, * 設定badge,設定聲音 */ Map<String, Object> extra = new HashMap<String, Object>(); IOSExtra iosExtra = new IOSExtra(10, "WindowsLogonSound.wav"); extra.put("ios", iosExtra); //對所有使用者傳送通知, 更多方法請參考文件 MessageResult msgResult = jpush.sendCustomMessageWithAppKey(sendNo,msgTitle, msgContent); //MessageResult msgResult = jpush.sendNotificationWithAlias(sendNo, "a", msgTitle, msgContent); //覆蓋指定msgId的訊息,msgId可以從msgResult.getMsgid()獲取。 //MessageResult msgResult = jpush.sendNotificationWithAppKey(sendNo, msgTitle, msgContent, 0, extra,msgResult.getMsgid()); if (null != msgResult) { System.out.println("伺服器返回資料: " + msgResult.toString()); if (msgResult.getErrcode() == ErrorCodeEnum.NOERROR.value()) { System.out.println(String.format("傳送成功, sendNo= %s,messageId= %s",msgResult.getSendno(),msgResult.getMsg_id())); } else { System.out.println("傳送失敗, 錯誤程式碼=" + msgResult.getErrcode() + ", 錯誤訊息=" + msgResult.getErrmsg()); } } else { System.out.println("無法獲取資料"); } } public static final int MAX = Integer.MAX_VALUE; public static final int MIN = (int) MAX/2; /** * 保持 sendNo 的唯一性是有必要的 * It is very important to keep sendNo unique. * @return sendNo */ public static int getRandomSendNo() { return (int) (MIN + Math.random() * (MAX - MIN)); } }
執行結果如下:
訊息接收端程式碼:
執行結果:package com.meritit.tuisong.service; import java.util.List; import cn.jpush.api.JPushClient; import cn.jpush.api.receive.ReceiveResult; public class ReceiveClientExample { private static final String appKey ="5d30bebd28335593a13454861"; //必填,例如466f7032ac604e02fb7bda89 private static final String masterSecret = "0e0cc80c6f6a4703bec9ed191";//"13ac09b17715bd117163d8a1";//必填 public static void main(String[] args) { JPushClient JPushClient = new JPushClient(masterSecret, appKey); String msgId = "1236722141"; String[] msgIds = {"1236722141","910981248","911034889"}; //獲取一條 ReceiveResult receiveResult = JPushClient.getReceived(msgId); if(receiveResult == null){ System.out.println("獲取receive 資料失敗!"+receiveResult); }else{ //gson toJson 之後,NULL值的欄位會被過濾掉 System.out.println("received result:"+receiveResult.toString()); } // 獲取多條 List<ReceiveResult> receiveResults = JPushClient.getReceiveds(msgIds); if(receiveResults == null ){ System.out.println("獲取receive 資料失敗!"); }else{ System.out.println("成功獲取了:"+receiveResults); } } }
測試已經成功,下面我們來看看原始碼是怎麼做的,其他的其實只是資料封裝,我們來看看關鍵的一句程式碼
MessageResult msgResult = jpush.sendCustomMessageWithAppKey(sendNo,msgTitle, msgContent);
檢視原始碼,如下:
public MessageResult sendCustomMessageWithAppKey(int sendNo, String msgTitle, String msgContent) {
CustomMessageParams p = new CustomMessageParams();
p.setReceiverType(ReceiverTypeEnum.APPKEYS);
return sendCustomMessage(p, sendNo, msgTitle, msgContent, null, null);
}
發現實際是呼叫的sendCustomMessage方法
protected MessageResult sendCustomMessage(CustomMessageParams p, int sendNo, String msgTitle, String msgContent, String msgContentType, Map<String, Object> extra) {
if (null != msgContentType) {
p.getMsgContent().setContentType(msgContentType);
}
if (null != extra) {
p.getMsgContent().setExtra(extra);
}
return sendMessage(p, sendNo, msgTitle, msgContent);
}
這裡進行了空值判斷,實際又呼叫了sendMessage方法
protected MessageResult sendMessage(MessageParams p, int sendNo, String msgTitle, String msgContent) {
p.setSendNo(sendNo);
p.setAppKey(this.getAppKey());
p.setMasterSecret(this.masterSecret);
p.setTimeToLive(this.timeToLive);
p.setSendDescription(this.getSendDescription());
for (DeviceEnum device : this.getDevices()) {
p.addPlatform(device);
}
if (null != msgTitle) {
p.getMsgContent().setTitle(msgTitle);
}
p.getMsgContent().setMessage(msgContent);
return sendMessage(p);
}
在這裡將引數封裝到訊息物件中呼叫sendMessage
protected MessageResult sendMessage(MessageParams params) {
return httpClient.sendPush(BaseURL.ALL_PATH, enableSSL, params);
}
再進到sendPush方法中看看,哦,大概明白了,實際上是用的http請求傳送訊息的。
public MessageResult sendPush(final String path, final boolean enableSSL, final MessageParams messageParams) {
MessageResult messageResult = ValidateRequestParams.vidateParams(messageParams);
if(messageResult != null) return messageResult;
String pushResult = sendPost(path, enableSSL, parse(messageParams),RequestTypeEnum.PUSH.value(),null);
return gson.fromJson(pushResult, MessageResult.class);
}
關鍵看倒數第二行程式碼
private String sendPost( String path, final boolean enableSSL, String params,Integer reqeustType,String authCode){
return sendRequest(path, enableSSL, params, "POST", reqeustType,authCode);
}
private String sendRequest(String path, final boolean enableSSL, String params,String method,Integer reqeustType,String authCode){
HttpURLConnection conn = null;
DataOutputStream outStream = null;
StringBuffer sb = new StringBuffer();
try {
if (enableSSL) {
initSSL();
}
URL url = new URL(BaseURL.getUrlForPath(path,enableSSL,reqeustType));
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT);
conn.setReadTimeout(DEFAULT_SOCKET_TIMEOUT);
conn.setUseCaches(false);
conn.setDoOutput(true);
conn.setRequestMethod(method);
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Charset", CHARSET);
if(authCode != null && !authCode.isEmpty()){
conn.setRequestProperty("Authorization", authCode);
}
if(method.equals("POST")){
byte[] data = params.getBytes(CHARSET);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(data.length));
outStream = new DataOutputStream(conn.getOutputStream());
outStream.write(data);
outStream.flush();
}
if (conn.getResponseCode() == 200) {
logger.info("Congratulations!The request was successful. response status is 200");
InputStream in = conn.getInputStream();
InputStreamReader reader = new InputStreamReader(in, CHARSET);
char[] buff = new char[1024];
int len;
while ((len = reader.read(buff)) > 0) {
sb.append(buff, 0, len);
}
} else {
logger.log(Level.WARNING,"Sorry!The request was fault. response " +
"status = "+conn.getResponseCode()+",errormsg = "+conn.getHeaderField(0));
String errmsg = "";
if(reqeustType == RequestTypeEnum.RECEIVE.value()){
errmsg = ErrorCodeEnum.errorMsg(conn.getResponseCode());
errmsg = errmsg == null ? conn.getHeaderField(0) : errmsg;
}else{
errmsg = conn.getHeaderField(0);
}
BaseResult result = new BaseResult(errmsg,conn.getResponseCode());
return result.toString();
}
}
catch (SocketTimeoutException e) {
logger.log(Level.SEVERE,"God! the server throw SocketTimeout Exception." +
"please check it out the error message:"+e.getMessage());
BaseResult baseResult = new BaseResult(e.getMessage().toString(),ErrorCodeEnum.CONNECTIONTIMEOUT.value());
return baseResult.toString();
}
catch (ConnectException e) {
logger.log(Level.SEVERE,"God! the server throw Connect Exception ." +
"please check it out the error message:"+e.getMessage());
BaseResult baseResult = new BaseResult(e.getMessage().toString(),ErrorCodeEnum.CONNECTIONREFUSED.value());
return baseResult.toString();
}
catch (UnknownHostException e) {
logger.log(Level.SEVERE,"God! the server throw UnknownHost Exception ." +
"please check it out the error message:"+e.getMessage());
BaseResult baseResult = new BaseResult(e.getMessage().toString(),ErrorCodeEnum.CONNECTIONREFUSED.value());
return baseResult.toString();
}
catch (Exception e) {
logger.log(Level.SEVERE,"God! the server throw exception." +
"please check it out the error message:"+e.getMessage());
BaseResult baseResult = new BaseResult(e.getMessage().toString(),ErrorCodeEnum.UnknownException.value());
return baseResult.toString();
}
finally {
if (null != outStream) {
try {
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != conn) {
conn.disconnect();
}
}
return sb.toString();
}
學過Android的朋友對這個應該很熟悉吧!比如裡面的URL請求地址,看第9行程式碼
URL url = new URL(BaseURL.getUrlForPath(path,enableSSL,reqeustType));
public static String getUrlForPath(final String path,boolean enableSSL,Integer type) {
return getHostname(enableSSL,type) + path;
}
private static String getHostname(boolean enableSSL,Integer type) {
if(type == RequestTypeEnum.PUSH.value())
return enableSSL? HOST_NAME_SSL :HOST_NAME;
if(type == RequestTypeEnum.RECEIVE.value())
return enableSSL? RECEIVE_HOST_NAME:RECEIVE_HOST_NAME;
return null;
}
在這裡進行判斷,如果enableSSL為false則傳送訊息請求地址為HOST_NAME,實際上這個enableSSL在BaseClient類中預設為false
public boolean enableSSL = false;
HOST_NAME就是官方文件中所說的預設請求地址:
public static String HOST_NAME = "http://api.jpush.cn:8800";
public static String RECEIVE_HOST_NAME = "https://report.jpush.cn:443";