今天在慕课网学习了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<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是,当检测到有消息要给刚上线的客户端发送的时候,调用发送方法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<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,其实思路很简单,就是重新发送消息的时候不再入库消息,而是取出之前的消息来发送,等收到客户端回执后再删除。
<p>版权声明:本文为博主原创文章,未经博主允许不得转载。</p>