SpringCloud工作筆記062---APP訊息推送_個推平臺API使用經驗
前言 移動Push推送是移動網際網路最基礎的需求之一,用於滿足移動互聯環境下訊息到達App客戶端。以轉轉(58趕集旗下真實個人的閒置交易平臺)為例,當買家下單後,我們通過移動Push推送訊息告訴賣家,當賣家已經發貨時,我們通過移動Push訊息告訴買家,讓買賣雙方及時掌握二手商品交易的實時訂單動態。 實現推送功能的方案有許多,具體可以看《程式設計師》的一篇文章http://geek.csdn.net/news/detail/58738,這裡也詳解了IOS與Android接受推送的機制不同。 本文主要講的是利用第三方平臺來開發個推功能的API,使用的個推平臺:http://www.getui.com/ 具體的使用流程,如何繫結APP在官網的官方文件裡已經詳細給出,這個都是操作問題,不涉及到後臺的開發。官方文件裡已經給出了API介紹,但是使用的需求API提供的資訊遠遠超出了API介紹的範圍。下面我將從設計的角度給出一個DEMO。 DEMO 需求:web端/APP端給指定群體使用者傳送一條通知,使用客戶端登入APP的指定使用者都能收到通知(支援多終端登入),在選單欄顯示通知,點選通知轉到APP內(透傳)顯示通知詳情(無論APP離線還是線上); 設計: 類: loginUserPojo(使用者類) NotificationPojo(通知類)--屬性見資料庫設計資訊表
資料庫表的設計: user_login(user_id,user_name,password....) nofitication(notification_id,title,content,createDt,creator_user_id....) user_device(user_id,client_id,device_token)(ios個推資訊通過device_id傳送,andorid通過client_id傳送)
個推API設計:
1.基本配置資訊類
public class GeXinBaseInfo { //以下三個屬性每個APP不同 static String appId = "VAn4M36ciG9mGBEQwuDxV5"; static String appkey = "HSlKLGNZ8e6RChB3JCIop9"; static String master = "CUEIVtxSdL6wO9GfRxFoZ1"; //host是固定的作為個推的伺服器 static String host = "http://sdk.open.api.igexin.com/serviceex"; static long offExpireTime = 24 * 1000 * 3600;
//透傳訊息設定,1為強制啟動應用,客戶端接收到訊息後就會立即啟動應用;2為等待應用啟動 static int TYPE_LAUNCH_APP = 1; static int TYPE_WAIT_FOR_CLICK = 2; }
2.安卓端接收資訊類(點選通知後,app端根據ID資訊獲取通知詳情)
public class TransimissionContentPojo implements Serializable{
//通知的id private String contentId;
//其它屬性。。。 //private ... public String getContentId() { return contentId; }
public void setContentId(String contentId) { this.contentId = contentId; } }
3.建立通知資訊的工廠類public class NotiTemplateFactory { //andorid public static NotificationTemplate produceNotiFromNoti(NotificationPojo notificationPojo){ NotificationTemplate template = getBaseTemplate(); template.setTitle("移動校園"); template.setText(notificationPojo.getTitle()); template.setTransmissionType(1); TransimissionContentPojo pojo = new TransimissionContentPojo(); pojo.setContentId(notificationPojo.getNotificationId()); template.setTransmissionContent(new Gson().toJson(pojo)); return template; } //ios public static APNPayload.DictionaryAlertMsg getDictionaryAlertMsg(String title, NotificationPojo nPojo){ APNPayload.DictionaryAlertMsg alertMsg = new APNPayload.DictionaryAlertMsg(); alertMsg.setBody(title); alertMsg.setTitle("移動校園"); alertMsg.setTitleLocKey("ccccc"); alertMsg.setActionLocKey("移動校園"); return alertMsg; } } 4.個推傳送資訊工具類
public class GeXinMPushUtil { private static GeXinMPushUtil instance;
private static ExecutorService executorService;
private List<Target> convertToTargets(List<String> cidList) { List<Target> targetList = new ArrayList<>(); for (String cid : cidList) { Target target = new Target(); target.setAppId(GeXinBaseInfo.appId); target.setClientId(cid); // target.setAlias(cid); targetList.add(target); } return targetList; }
protected IGtPush push;
public GeXinMPushUtil() { push = new IGtPush(GeXinBaseInfo.host, GeXinBaseInfo.appkey, GeXinBaseInfo.master); executorService = Executors.newCachedThreadPool(); }
public static GeXinMPushUtil getInstance() { if (instance == null) { instance = new GeXinMPushUtil(); } return instance; } //andorid有通知 public void push(final NotificationTemplate notificationTemplate, final List<String> cidList) {
executorService.submit(new Runnable() { @Override public void run() { ListMessage message = new ListMessage(); message.setData(notificationTemplate); message.setOffline(true); message.setOfflineExpireTime(GeXinBaseInfo.offExpireTime); String taskId = push.getContentId(message); IPushResult ret = push.pushMessageToList(taskId, convertToTargets(cidList)); System.out.println(ret.getResponse().toString()); } }); } //andorid透傳,無通知 public void push(final TransmissionTemplate transmissionTemplate, final List<String> cidList) {
executorService.submit(new Runnable() { @Override public void run() { ListMessage message = new ListMessage(); message.setData(transmissionTemplate); message.setOffline(false); message.setOfflineExpireTime(GeXinBaseInfo.offExpireTime); String taskId = push.getContentId(message); IPushResult ret = push.pushMessageToList(taskId, convertToTargets(cidList)); System.out.println(ret.getResponse().toString());
} }); }
//將使用者ID與client_id繫結記錄在個推服務上 public boolean bind(String alias, String cid){ IAliasResult bindSCid = push.bindAlias(GeXinBaseInfo.appId, alias, cid); return bindSCid.getResult(); }
//解綁 public boolean unBind(String alias, String cid){ IAliasResult unBindSCid = push.unBindAlias(GeXinBaseInfo.appId, alias, cid); return unBindSCid.getResult(); } //ios推送 public void pushAPN(final APNPayload.DictionaryAlertMsg alertMsg , final List<String> deviceTokens, String content){ IGtPush push = new IGtPush(GeXinBaseInfo.host, GeXinBaseInfo.appkey, GeXinBaseInfo.master);
APNTemplate t = new APNTemplate();
APNPayload apnpayload = new APNPayload(); apnpayload.setSound("");
apnpayload.setAlertMsg(alertMsg); //傳送的附加資訊,用於解析 apnpayload.addCustomMsg("info",content); t.setAPNInfo(apnpayload); ListMessage message = new ListMessage(); message.setData(t); IPushResult ret = push.pushAPNMessageToList(GeXinBaseInfo.appId, contentId, deviceTokens); System.out.println(ret.getResponse()); } }
邏輯設計: 使用者登入andorid攜帶client_id,iOS端攜帶device_token,檢查user_device中是否有記錄user_id與 client_id或user_id與device_token的組合,如果沒有插入組合,並且繫結這組資訊
public UserInfoJson login(HttpServletRequest request, HttpServletResponse response) { String userName = request.getParameter("userName"); String password = request.getParameter("passWord"); String clientId = request.getParameter("clientId"); String deviceId = request.getParameter("deviceToken"); LoginUserPojo user = loginUserService.findByUserCode(userName); if (user == null) { return new UserInfoJson(); } else { UserInfoJson userJson = getUserJson(user); //繫結使用者與硬體 if (clientId != null && !clientId.trim().equals("")) { userJson.setClientId(clientId); int count = loginUserService.selectDeviceByClientId(user.getUserId(), clientId); if (count == 0) { GeXinMPushUtil.getInstance().bind(user.getUserId(), clientId); loginUserService.insertDeviceByClientId(user.getUserId(), clientId); } } if (deviceId != null && !deviceId.trim().equals("")) { userJson.setDeviceId(deviceId); int count = loginUserService.selectDeviceByDeviceId(user.getUserId(), deviceId); if (count == 0) { loginUserService.insertDeviceByDeviceId(user.getUserId(), deviceId); } } String encPassword = new SimpleHash("md5", password, new SimpleByteSourceFix(user.getSalt()), 2).toHex(); if (!encPassword.equals(user.getPassword())) { return new UserInfoJson(); } else { userJson.setToken(encPassword); return userJson; } } }
登出登入:
public boolean loginOut(HttpServletRequest request, HttpServletResponse response) { String userId = request.getParameter("userId"); String clientId = request.getParameter("clientId"); String deviceId = request.getParameter("deviceToken"); boolean result = false; //取消繫結 if (clientId != null && !clientId.trim().equals("")) loginUserService.deleteDeviceByClientId(userId, deviceId); result = GeXinMPushUtil.getInstance().unBind(userId, clientId); if (deviceId != null) { try { loginUserService.deleteDeviceByDeviceId(userId, deviceId); result = true; } catch (Exception e){ result = false; e.printStackTrace(); } }
return result; }
在傳送通知的方法中,加入推送的程式碼:
..............前面的程式碼使用者獲取NotificationPojo nPojo(通知內容), List<String> sendUsers(傳送的使用者ID) List<String> listAlias = new ArrayList<>(); if (sendUsers != null && !sendUsers.isEmpty()) { for (LoginUserPojo userPojo : sendUsers) { // TODO: 16/1/26 getui these users listAlias.add(userPojo.getUserId()); } } List<String> deviceTokens = new ArrayList<>(); List<String> clientIds = new ArrayList<>(); if (listAlias != null && !listAlias.isEmpty() && listAlias.size() != 0) { deviceTokens = loginUserService.selectDeviceTokens(listAlias); clientIds = loginUserService.selectClientIds(listAlias); } TransimissionContentPojo pojo = new TransimissionContentPojo(TransimissionContentPojo.TYPE_NOTI); pojo.setContentId(notificationPojo.getNotificationId()); NotificationTemplate template = NotiTemplateFactory.produceNotiFromNoti(notificationPojo); if (clientIds.size() != 0 && !clientIds.isEmpty()) GeXinMPushUtil.getInstance().push(template, clientIds); APNPayload.DictionaryAlertMsg alertMsg = NotiTemplateFactory.getDictionaryAlertMsg(notificationPojo.getTitle() ,notificationPojo); if (deviceTokens.size() != 0 && !deviceTokens.isEmpty()) GeXinMPushUtil.getInstance().pushAPN(alertMsg, deviceTokens,new Gson().toJson(pojo));
.................這樣就可以將推送資訊傳送出去了
其中 selectDeviceTokens(List<String> alias)的mybatis程式碼如下:
(使用MyBatis的sql如下 <select id="selectDeviceTokens" resultType="java.lang.String"> select device_id from user_device where device_id is not null and user_id in <foreach collection="userIds" item="userId" index="index" open="(" close=")" separator=","> #{userId} </foreach> </select>)
對於附加資訊,contentId的傳送,app的獲取,安卓端的獲取凡事:msg.content-------此內容為new Gson().toJson(pojo)的Json字串,可以解析獲取contentId的內容,然後更具contentId獲取Notification的詳情(傳送一次請求,sql查詢) 而IOS的處理比較複雜,msg.payload.info-----此內容為new Gson().toJson(pojo)的Json字串,因為IOS服務端傳送的是ListMessage,這是對應msg,listMessage設定了payLoad,payLoad設定了info(apnpayload.addCustomMsg("info",content);),因此提取額外資訊是msg.payload.info,info這個字端是在伺服器端設定的,當然也可以是其它名稱。 --------------------- 作者:VICHOU_FA 來源:CSDN 原文:https://blog.csdn.net/vichou_fa/article/details/50802222 版權宣告:本文為博主原創文章,轉載請附上博文連結!