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

转帖请注明本文出自pkxutao的博客(http://blog.csdn.net/pkxutao/article/details/22653903),请尊重他人的辛勤劳动成果,谢谢!

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

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



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

在进入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这些实例或者方法请直接忽略,只是个人定义的方法,对整体理解没影响。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值