首页 > 代码库 > Android推送进阶课程学习笔记

Android推送进阶课程学习笔记

今天在慕课网学习了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回执后再删除。
<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

Android推送进阶课程学习笔记