標籤:筆記 效果 記錄 問題 article save log split ble
今天在慕課網學習了Android進階課程推送的server端處理回執的訊息 。
這集課程主要介紹了,當server往client推送訊息的時候,client須要發送一個回執回來確認收到了推送訊息才算一次完整的推送過程。
詳細的實現方法為server推送一個訊息到client的時候,會產生一個相應的uuid標識這個訊息,並把這個訊息以及uuid儲存到資料庫中。client收到訊息後,取出當中的uuid並將這個uuid發給server端,服務端收到這個uuid。依據uuid到資料庫裡刪除了相應的訊息記錄,整個推送算完畢。
這裡先貼出比較核心的發送代碼
public void sendNotifcationToUser(String apiKey, String username, String title, String message, String uri) { log.debug("sendNotifcationToUser()..."); Random random = new Random(); //這個id就是client發送回執相應的uuid String id = Integer.toHexString(random.nextInt()); IQ notificationIQ = createNotificationIQ(id, apiKey, title, message, uri); ClientSession session = sessionManager.getSession(username); if (session != null) { if (session.getPresence().isAvailable()) { notificationIQ.setTo(session.getAddress()); session.deliver(notificationIQ); } else{ saveNotification(apiKey, username, title, message, uri, id); } } //無論使用者存在不存在都須要將訊息存入資料庫,直到使用者收到訊息發送回饋之後再刪除 try { User user = mUserService.getUserByUsername(username); if(null != user){ saveNotification(apiKey, username, title, message, uri, id); } } catch (UserNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
能夠看到,每次推送訊息給client的時候都會做入庫操作。
同一時候,源碼裡還有個商務邏輯,當server端檢測到client從離線到上線狀態的時候,會去資料庫尋找是否有該客戶的的訊息,有的話就會取出來發送,代碼例如以下
List<Notification> list = mNotificationSevice.findNotificationByUsername(session.getUsername()); if(null != list && list.size() > 0){ for(Notification notification: list){ String apiKey = notification.getApiKey(); String title = notification.getTitle(); String message = notification.getMessage(); String uri = notification.getUri(); mNotificationManager.sendNotifcationToUser(apiKey, session.getUsername(), title, message, uri); mNotificationSevice.deleteNotification(notification); } }
這個代碼存在的一個bug是,當檢測到有訊息要給剛上線的client發送的時候。調用發送方法sendNotifcationToUser。並從資料庫刪除掉了原來的訊息。這樣操作後,會發如今sendNotifcationToUser裡入庫的訊息被
mNotificationSevice.deleteNotification(notification);也一起刪除了(當然原來的入庫的訊息也一起刪除,但這個刪除是正確的),而剛剛入庫的那條訊息是不應該刪除的,必須等client發送回執回來後再刪除。
視頻作者郭神對這個bug的解決方案例如以下。先直接貼出代碼
public void sendNotifcationToUser(String apiKey, String username, String title, String message, String uri, boolean shouldSave) { log.debug("sendNotifcationToUser()..."); Random random = new Random(); //這個id就是client發送回執相應的uuid String id = Integer.toHexString(random.nextInt()); IQ notificationIQ = createNotificationIQ(id, apiKey, title, message, uri); ClientSession session = sessionManager.getSession(username); if (session != null) { if (session.getPresence().isAvailable()) { notificationIQ.setTo(session.getAddress()); session.deliver(notificationIQ); } else{ saveNotification(apiKey, username, title, message, uri, id); } } //無論使用者存在不存在都須要將訊息存入資料庫,直到使用者收到訊息發送回饋之後再刪除 try { User user = mUserService.getUserByUsername(username); if(null != user && shouldSave){ saveNotification(apiKey, username, title, message, uri, id); } } catch (UserNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
以上代碼添加了一個欄位shouldSave來推斷是否入庫,同一時候在檢測到client上線而且資料庫有之前發送失敗的訊息得推送的時候,傳入false
if(null != list && list.size() > 0){ for(Notification notification: list){ String apiKey = notification.getApiKey(); String title = notification.getTitle(); String message = notification.getMessage(); String uri = notification.getUri(); mNotificationManager.sendNotifcationToUser(apiKey, session.getUsername(), title, message, uri, false); mNotificationSevice.deleteNotification(notification); } }
這樣改完測了之後,發現沒有不論什麼問題,client從離線到上線後,原本存在資料庫的訊息都沒有了,滿足了需求。
可是。事實上是有問題的,當client從離線到上線而且server端從資料庫檢測到有訊息得推送的時候,由於傳入sendNotifcationToUser的最後一個參數是false,根本沒有做入庫操作。所以資料庫根本沒有這條發送訊息的資料,client收到訊息發送回執後,server沒有相應的資料能夠刪除,導致看起來似乎達到了預期的效果。
針對這個問題。我做的改動例如以下,針對client從離線到線上的狀態並須要推送之前為推送成功的訊息,從資料庫取出資料,直接推送該訊息,不刪除該訊息。也不再插入新訊息,等收到client回執後再刪除。
public void sendNotifcationToUser(String id, String apiKey, String username, String title, String message, String uri, boolean shouldSave) { log.debug("sendNotifcationToUser()..."); IQ notificationIQ = createNotificationIQ(id, apiKey, title, message, uri); ClientSession session = sessionManager.getSession(username); if (session != null) { if (session.getPresence().isAvailable()) { notificationIQ.setTo(session.getAddress()); session.deliver(notificationIQ); } else if(shouldSave){ saveNotification(apiKey, username, title, message, uri, id); } } //無論使用者存在不存在都須要將訊息存入資料庫,直到使用者收到訊息發送回饋之後再刪除 try { User user = mUserService.getUserByUsername(username); if(null != user && shouldSave){ saveNotification(apiKey, username, title, message, uri, id); } } catch (UserNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
這裡還多了id欄位,每次發送訊息,id訊息都是產生一個新的。對於發送之前的訊息。全然不是必需產生新的id(即uuid),取出原來訊息的id即可了,尋找訊息的地方改為例如以下
List<Notification> list = mNotificationSevice.findNotificationByUsername(session.getUsername()); if(null != list && list.size() > 0){ for(Notification notification: list){ String apiKey = notification.getApiKey(); String title = notification.getTitle(); String message = notification.getMessage(); String uri = notification.getUri(); String id = notification.getUuid(); mNotificationManager.sendNotifcationToUser(id, apiKey, session.getUsername(), title, message, uri, false); } }這樣就能夠避免作者郭神的bug。事實上思路非常easy。就是又一次發送訊息的時候不再入庫訊息,而是取出之前的訊息來發送。等收到client回執後再刪除。
Android推送進階課程學習筆記