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

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

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

轮询信息管理器——轮询+主动拉取_第1张图片
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 params = new HashMap();
        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 params = new HashMap();
        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这些实例或者方法请直接忽略,只是个人定义的方法,对整体理解没影响。

你可能感兴趣的:(轮询信息管理器——轮询+主动拉取)