個推平臺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這個字端是在服務器端設置的,當然也可以是其它名稱。



         關注微信公衆號每天學習一點程序員的職業經


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章