项目中为了改善用户体验,一些耗时操作常常会使用异步方式执行,但是一旦将任务放入后台,用户就不能及时得到任务执行的结果。所以需要涉及一种方式能够持续跟踪任务的进行情况。每个监听任务会被分配一个任务ID,在任务监听结束后被释放。
全局监听器的实现
基于单例模式设置一个HashMap能被系统全局访问用于存放任意任务的监听数据。同时Value部分使用特殊特殊结构。
结构设计
即HashMap中的Value存储的部分。
public class ValueStruc {
private boolean expireable;
private Date expireTime;
private Object value;
private int readCount;
public ValueStruc(){}
public ValueStruc(Object value, Date expireTime){
this.value = value;
this.expireable = true;
this.expireTime = expireTime;
this.readCount = 0;
}
public ValueStruc(Object value){
this.value = value;
this.readCount = 0;
}
public Object readValue(){
readCount = readCount +1;
return this.value;
}
public boolean expired(){
if(expireable){
return new Date().after(expireTime);
} else {
return false;
}
}
public boolean isExpireable(){
return this.expireable;
}
public void setExpireable(boolean exb){
this.expireable = exb;
}
public Date getExpireTime(){
return expireTime;
}
public void setExpireTime(Date exd){
this.expireTime = exd;
}
public Object getValue(){
return value;
}
public void setValue(Object v){
this.value = v;
}
public int getReadCount(){
return readCount;
}
public void setReadCount(int readCount){
this.readCount = readCount;
}
}
监听器
用于在程序的任意位置注册监听ID,更新、读取ID对应的信息。同时应该支持ID的过期清除,读取行为记录等。
代码如下:
public class DataObserver {
private HashMap<String, ValueStruc> map;
private volatile static DataObserver instance;
private DataObserver(){
map = new HashMap<String, ValueStruc>();
}
public static DataObserver getInstance(){
if(instance == null){
synchronized(DataObserver.class){
if(instance == null){
instance = new DataObserver();
}
}
}
return instance;
}
/**
* 注册Key<br>
* 基本:输入键值对,key, value<br>
* 可选:如果需要有有效时间,则加入秒数,否则写入负数即可表示为不设置过期时间,0没有意义<br>
* Function: register
*
* @author wengshengyuan DateTime 2015-10-12 上午9:16:01
* @param key
* @param value
* @param Sec
* @return ValueStruc,已存在该key则返回null
*/
public ValueStruc register(String key, Object value, int Sec) {
if(map.containsKey(key))
return null;
Date now = new Date();
if(Sec > 0){
Date expireTime = DateUtils.addSeconds(now, Sec);
ValueStruc v = new ValueStruc(value, expireTime);
map.put(key, v);
return v;
} else {
ValueStruc v = new ValueStruc(value);
map.put(key, v);
return v;
}
}
/**
* 更新已存在的值
* Function: update
*
* @author wengshengyuan DateTime 2015-10-12 下午1:45:46
* @param key
* @param value
* @return 返回新的值,若没有该Key, 返回null
*/
public ValueStruc update(String key, Object value){
if(map.containsKey(key)){
ValueStruc v = map.get(key);
v.setValue(value);
map.put(key, v);
return v;
} else {
return null;
}
}
/**
* 读取key<br>
* Function: read
*
* @author wengshengyuan DateTime 2015-10-12 上午9:29:48
* @param key
* @return 写入的Object, 过期或者没有均返回null
*/
public Object read(String key){
ValueStruc v = map.get(key);
if(v == null)
return null;
if(v.expired()){
map.remove(key);
return null;
}
return v.readValue();
}
/**
* 删除某一Key的值<br>
* 若删除成功,则返回之前的值,若删除不成功,则返回null
* Function: remove
*
* @author wengshengyuan DateTime 2015-10-12 上午11:09:39
* @param key
* @return
*/
public ValueStruc remove(String key){
return map.remove(key);
}
/**
* 清空map
* Function: clear
*
* @author wengshengyuan DateTime 2015-10-12 上午11:10:23
*/
public void clear(){
map = new HashMap<String, ValueStruc>();
}
}
使用方式
注册
在执行异步方法前,生成监听ID, 并传入异步执行体。执行状态的更新由异步任务更新。
String observerID = "taskName_"+UUIDUtils.getUUID32();
DataObserver.getInstance().register(observerID,value,-1);
AsyncTask task = new AsyncTask(String observerID);
task.start();
更新
在异步任务中随时调用
DataObserver.getInstance().update(observerID, value);
读取
DataObserver.getInstance().read(observerID);
Controller层接口
JAVA服务端API实现
通过Controller定义数据传输接口。包括:增、删、改、查和清除等功能。这里以查询为例。
Controller代码如下:
@Controller
@RequestMapping(value="/api/observer")
public class DataObserverAPI {
@ResponseBody
@RequestMapping(value = "observe/{key}", method = RequestMethod.GET)
public ResultInfo observe(HttpServletRequest request, @PathVariable("key") String key){
ResultInfo result = new ResultInfo();
Object o = DataObserver.getInstance().read(key);
if(o == null){
result.setStateId(-1);
result.setErrorMsg("未查询到信息");
} else {
result.addObj2Map("value", o);
}
return result;
}
@ResponseBody
@RequestMapping(value = "regester/{key}/{value}/{timeout}", method = RequestMethod.GET)
public ResultInfo testObserver(HttpServletRequest request, @PathVariable("key") String key, @PathVariable("value") String value
, @PathVariable("timeout") int timeout){
ResultInfo result = new ResultInfo();
ValueStruc v = DataObserver.getInstance().regester(key, value, timeout);
if(v == null){
result.setStateId(-1);
result.setErrorMsg("插入失败");
} else {
result.addObj2Map("value", v);
}
return result;
}
@ResponseBody
@RequestMapping(value = "update/{key}/{value}", method = RequestMethod.GET)
public ResultInfo update(HttpServletRequest request, @PathVariable("key") String key, @PathVariable("value") String value){
ResultInfo result = new ResultInfo();
ValueStruc v = DataObserver.getInstance().update(key, value);
if(v == null){
result.setStateId(-1);
result.setErrorMsg("更新失败");
} else {
result.addObj2Map("value", v);
}
return result;
}
@ResponseBody
@RequestMapping(value="remove/{key}", method = RequestMethod.GET)
public ResultInfo clear(HttpServletRequest request, @PathVariable("key") String key){
ResultInfo result = new ResultInfo();
ValueStruc v = DataObserver.getInstance().remove(key);
result.addObj2Map("value", v);
return result;
}
@ResponseBody
@RequestMapping(value = "clear", method = RequestMethod.GET)
public void clear(){
DataObserver.getInstance().clear();
}
}
前端js实现
以通过接口调用服务器执行ping命令,并监听ping的结果为例,js代码如下(以下结合AngularJs,其他脚本亦类似):
第一步:调用异步服务并获取监听ID
通过约定,在调用异步任务发起后返回注册的监听ID。
//调用服务
this.ping = function($scope){
var url = ctx + 'godmode/ping';
var data = {
ip : $scope.pingIP
};
$.ajax({
type:"post",
url:url,
async:false,
data :data,
success: function(r){
console.log(r);
$scope.observerID = r.map.observerID[0];
},
error:function(){
$interva
}
});
}
第二步:监听ID数据
//定时调用监听
var timer = $interval(function(){
myService.observePing($scope,$interval);
},1000);
//监听服务
this.observePing = function($scope,$interval){
console.log('observing:'+$scope.observerID);
$.ajax({
type : "get",
url: ctx + "api/observer/observe/"+$scope.observerID,
dataType: "json",
async: true,
timeout: 10000,
error: function(r) {
console.log('canceling observer');
$interval.cancel(timer);
},
success: function(r) {
if (r.stateId == 0) {
var text = "";
$.each(r.map.value[0], function(index, item) {
text = text + item + "\n";
});
$scope.pingResults = text;
} else {
//监听资源释放后,停止interval
console.log('canceling observer');
$interval.cancel(timer);
}
}
});
}