編輯:關於Android編程
今天在慕課網學習了Android進階課程推送的服務器端處理回執的消息 。這集課程主要介紹了,當服務器往客戶端推送消息的時候,客戶端需要發送一個回執回來確認收到了推送消息才算一次完整的推送過程。
具體的實現方法為服務器推送一個消息到客戶端的時候,會生成一個相應的uuid標識這個消息,並把這個消息以及uuid存儲到數據庫中,客戶端收到消息後,取出其中的uuid並將這個uuid發給服務器端,服務端收到這個uuid,根據uuid到數據庫裡刪除了對應的消息記錄,整個推送算完成。這裡先貼出比較核心的發送代碼
public void sendNotifcationToUser(String apiKey, String username,
String title, String message, String uri) {
log.debug("sendNotifcationToUser()...");
Random random = new Random();
//這個id就是客戶端發送回執對應的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();
}
}
可以看到,每次推送消息給客戶端的時候都會做入庫操作。
同時,源代碼裡還有個業務邏輯,當服務器端檢測到客戶端從離線到上線狀態的時候,會去數據庫查找是否有該客戶的的消息,有的話就會取出來發送,代碼如下
List 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是,當檢測到有消息要給剛上線的客戶端發送的時候,調用發送方法sendNotifcationToUser,並從數據庫刪除掉了原來的消息,這樣操作後,會發現在sendNotifcationToUser裡入庫的消息被
mNotificationSevice.deleteNotification(notification);也一起刪除了(當然原來的入庫的消息也一起刪除,但這個刪除是正確的),而剛剛入庫的那條消息是不應該刪除的,必須等客戶端發送回執回來後再刪除。
視頻作者郭神對這個bug的解決方法如下,先直接貼出代碼
public void sendNotifcationToUser(String apiKey, String username,
String title, String message, String uri, boolean shouldSave) {
log.debug("sendNotifcationToUser()...");
Random random = new Random();
//這個id就是客戶端發送回執對應的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來判斷是否入庫,同時在檢測到客戶端上線並且數據庫有之前發送失敗的消息得推送的時候,傳入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);
}
}
這樣改完測了之後,發現沒有任何問題,客戶端從離線到上線後,原本存在數據庫的消息都沒有了,滿足了需求。
但是,其實是有問題的,當客戶端從離線到上線並且服務器端從數據庫檢測到有消息得推送的時候,因為傳入sendNotifcationToUser的最後一個參數是false,根本沒有做入庫操作,所以數據庫根本沒有這條發送消息的數據,客戶端收到消息發送回執後,服務器沒有對應的數據可以刪除,導致看起來似乎達到了預期的效果。
針對這個問題,我做的修改如下,針對客戶端從離線到在線的狀態並需要推送之前為推送成功的消息,從數據庫取出數據,直接推送該消息,不刪除該消息,也不再插入新消息,等收到客戶端回執後再刪除。
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 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,其實思路很簡單,就是重新發送消息的時候不再入庫消息,而是取出之前的消息來發送,等收到客戶端回執後再刪除。
動態加載是什麼應用在運行的時候通過加載一些本地不存在的可執行文件實現一些特定的功能,Android中動態加載的核心思想是動態調用外部的Dex文件,極端的情況下,Andro
Android之Http通信——1.初識Http協議Android之Http通信1初識Http協議 引言 正文 Http是什麼鬼 名詞解析 Htt
前言:關於servlet相信學過java的都不會陌生,我最近又把這些基礎知識整理一遍,便於自已能更好的理解ssm或者ssh,下面開始1:Servlet接口servlet有
Earthquake(地震顯示器) 項目 詳解 環境: Android Studio 0.5.2, Gradle 1.11, kindle f