很多应用中,都需要一进入应用就启动一个线程轮询检查服务器信息,如果有新消息就给出通知,最常见的就是底部导航栏上的小红点了,最近项目中需要这个功能,初期做法很简单,效果也不理想,这个版本正好留出时间修改,用单例+观察者模式终于实现出一个自己比较满意的效果,下面讲讲两个方法的实现。
设计图:(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这些实例或者方法请直接忽略,只是个人定义的方法,对整体理解没影响。
网友评论