美文网首页
轮询信息管理器——轮询+主动拉取

轮询信息管理器——轮询+主动拉取

作者: pkxutao | 来源:发表于2017-11-29 14:54 被阅读10次

    很多应用中,都需要一进入应用就启动一个线程轮询检查服务器信息,如果有新消息就给出通知,最常见的就是底部导航栏上的小红点了,最近项目中需要这个功能,初期做法很简单,效果也不理想,这个版本正好留出时间修改,用单例+观察者模式终于实现出一个自己比较满意的效果,下面讲讲两个方法的实现。

    设计图:(word画的,太丑了,将就下)

    image

    [图片上传失败...(image-85cc93-1511938268747)]

    在上一个版本中,我是这样做的:

    在进入MainActivity时,开启一个线程,轮询请求服务器数据:

    //开启轮询线程请求信息
        public void checkUpdateTip( final Context context,final CheckNewCallback checkNewCallback){
            _stopRequested = false;
            if (null == _checkNewTip) {
                _checkNewTip = new CheckNewTip(context,checkNewCallback);
                _checkNewTip.getDataPoll();
            }
        }
        
        //主动请求信息(不等待轮询)
        public void checkUpdateTipNow() {
            if (null != _checkNewTip) {
                _checkNewTip.getDataNow();
            }
        }
        
        //取消轮询
        public void cancelCheckThread(){
            _stopRequested = true;
        }
        
        private class CheckNewTip{
            Handler mHandler = new Handler(){
                public void handleMessage(android.os.Message msg) {
                    switch (msg.what) {
                    case 0:
                        checkNewCallback.onSucc(findData);
                        break;
    
                    default:
                        break;
                    }
                };
            };
            Context context;
            CheckNewCallback checkNewCallback;
            FindData findData;
            
            public CheckNewTip(final Context context,final CheckNewCallback checkNewCallback) {
                this.context                = context;
                this.checkNewCallback       = checkNewCallback;
                _stopRequested              = false;
                findData                    = new FindData();
            }
            //轮询线程
            private void getDataPoll() {
                new Thread(){
                    public void run() {
                        while (!_stopRequested) {
                            findData = getData(context, findData, mHandler);
                            if (null != findData) {
                                mHandler.sendEmptyMessage(0);
                            }
                            try {
                                sleep(GET_FROM_NETWORK_INTERVAL);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }
                }.start();
            }
            //主动请求线程
            private void getDataNow() {
                new Thread(){
                    public void run() {
                        findData = getData(context, findData, mHandler);
                        if (null != findData) {
                            mHandler.sendEmptyMessage(0);
                        }
                    }
                }.start();
            }
            
                
        }
        
        
        private FindData getData(Context context, FindData findData, Handler mHandler) {
            Map<String, String> params = new HashMap<String, String>();
            AccountSystem accountSystem = new AccountSystem(context);
            Account account = accountSystem.getAccount();
            params.put("auth", account.getAuth());
            params.put("pauth", account.getPauth());
            
            String result = GmqHttpUtil.post(_context, GET_MESSAGE_NUM_URL, params);//开始请求数据
            if (null != result) {
                findData = parseFindMsg(result);//解析数据并赋给实例
                return findData;
            }else{
                return null;
            }
        }
        public interface CheckNewCallback{//回调接口
            public void onSucc(FindData findData);
        }
    调用时:
                    _mainLogic.checkUpdateTip(MainActivity.this,
                            new CheckNewCallback() {
                                @Override
                                public void onSucc(FindData findData) {
                                    doSomeThing();
                                }
    
                            });
    

    (忽略FindData吧,那是一个实例,捆绑获取到的数据)。

    得到数据后,就可以根据数据判断是否显示小红点。当点击有小红点的模块时,相应的模块也要根据这个数据做一些UI上的提示,怎样在对应的Activity得到这个数据呢(FindData为MainActivity下的对象)?建议先看我的另一篇博客:http://blog.csdn.net/pkxutao/article/details/19410143,也就是通过getParentActivity().findData就可以得到这个实例了(getParentActivity强制为MainActivity),有人肯定会有疑惑:为什么不把FindData这个实例声明为public static,这样获取就简单多了,只能说这样非常不安全,并且这个实例应该依赖MainActivity对象(自己理解的不够深,说不太清楚,以后补充)。

    现在有个需求是当点击对应模块时,需要主动拉取服务器数据,这个时候可以通过getParentActivity得到MainActivity对象,然后通过Mainactivity对象调用getDataNow()去请求数据,这里得到数据后都是通过handler来执行回调,避免出现不同线程同时操作统一数据的危险。当通过调用

    getDataNow()获取数据后怎样改变当前Activity 的UI呢?这就是MainActivity怎样得到子Activity对象的问题,还是看刚刚介绍的那篇博客,得到子Activity后就可以任意操作UI了。至此,需求满足了,但总觉得别扭。

    别扭1:子Activity竟然根据MainActivity的数据来更新自己的UI
    
    别扭2:子Activity的UI改变竟然是由MainActivity来改变
    
    别扭3:有些深层次的Activity不能通过getParentActivity得到MainActivity实例
    

    最近正好在看设计模式书(HeadFirst设计模式,强烈推荐),看到单例和观察者模式,正好符合现在的需求,动手之:

    新建MessageData类,单例模式:

    /*
         * 构造函数声明为private的原因是因为不让外部通过new MessageData得到MessageData,
         * 保持MessageData只有一个实例
        */
        private  MessageData() {
            
        }
        
        
        
        public static synchronized MessageData instance() {
            if(null == _instance) {
                _instance   = new MessageData();
            }
            return _instance;
        }
    
    观察者模式:
    public class MessageData extends Observable
    
    获取信息:
    Handler _handler = new Handler(){
            public void handleMessage(android.os.Message msg) {
                switch (msg.what) {
                case 0:
                    SwitchLogger.d(LOG_TAG, "refresh findata notify all observers");
                    setChanged();
                    notifyObservers(_findData);//通知观察者数据已改变
                    break;
    
                default:
                    break;
                }
            };
        };
        
        public void cancelCheckThread(){
            _stopRequested = true;
            SwitchLogger.d(LOG_TAG, "cancelCheckThread");
        }
        
        //轮询请求
        public void getDataPoll(final Context context) {
            SwitchLogger.d(LOG_TAG, "getdata poll");
            _stopRequested = false;
            new Thread(){
                public void run() {
                    while (!_stopRequested) {
                        _findData = getData(context);
                        if (null != _findData) {
                            _handler.sendEmptyMessage(0);
                        }
                        try {
                            sleep(GET_FROM_NETWORK_INTERVAL);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        }
            
        //主动请求
        public void getDataNow(final Context context) {
            SwitchLogger.d(LOG_TAG, "getdata now");
            new Thread(){
                public void run() {
                    _findData = getData(context);
                    if (null != _findData) {
                        _handler.sendEmptyMessage(0);
                    }
                }
            }.start();
        }
        
        
        private FindData getData(Context context) {
            Map<String, String> params = new HashMap<String, String>();
            AccountSystem accountSystem = new AccountSystem(context);
            Account account = accountSystem.getAccount();
            params.put("auth", account.getAuth());
            params.put("pauth", account.getPauth());
            
            String result = GmqHttpUtil.post(context, GET_MESSAGE_NUM_URL, params);
            SwitchLogger.d(LOG_TAG, "request msg tip:" + result);
            if (null != result) {
                
                FindData findData = parseFindMsg(result);
                return findData;
            }else{
                return null;
            }
        }
    

    在需要获取数据的地方,即观察者:

    1、实现Observer接口

    2、重写update方法

    @Override
        public void update(Observable observable, Object data) {
            doSomeThing();
    }
    

    这样,在MessageData里调用notifyObservers的时候就可以接收到参数,强转为FindData实例:FindData findData = (FindData)data;

    使用方法:

    1、通过MessageData.instance().addObserver(this)把自己添加为观察者

    2、重写update接收数据

    3、通过

    MessageData.

    getDataNow(this)主动请求数据,数据结果会回调给update

    这样写的话,子Activity就可以通过实现Observer来获取轮询的数据,不依赖于MainActivity。MainActivity数据和子Activity数据是完全分开的,MainActivity数据只用来判断显示小红点,子Activity数据用来更新UI界面,这样就解决了上面所有的别扭,逻辑也非常清楚,松耦合。

    这篇博客就不放Demo了,只讲实现思路。

    注:上面一些像FindData、AccountSystem这些实例或者方法请直接忽略,只是个人定义的方法,对整体理解没影响。

    相关文章

      网友评论

          本文标题:轮询信息管理器——轮询+主动拉取

          本文链接:https://www.haomeiwen.com/subject/xfnubxtx.html