问题背景
想象这么一种场景,我们想要异步去请求服务器的数据,并且约定跟服务器使用json格式进行通信,当有回调时再切换到主线程给我们回调。在不使用框架的情况下我们应该如何实现呢?
1 添加回调接口,常见的定义形式如下:
public interface RequestCallBack{
/**
* 成功接受到数据被调用
* @param data json格式的数据
*/
void onResponse(String data);
void onError(int errorCode,String errorDesp);
}
2 异步请求数据并且将callBack保存(实际上需要会话管理、线程池管理等等,与本文无关,不解释那些):
private RequestCallBack mRequestCallBack;
/**
* 请求数据
* @param requestData requestData
* @param callBack 收到response或者出错时的回调
*/
public void requestData(String requestData, RequestCallBack callBack) {
mRequestCallBack = callBack;
// 异步执行request
new Thread(){
@Override
public void run() {
// do request
}
}.start();
}
3 在得到response的时候切换到主线程进行回调:
/**
* 主线程分发对应的response
* @param response
*/
public void dispatchResponseOnUi(String response){
mRequestCallBack.onResponse(response);
}
那看下我们正常的请求的地方的常见写法:
/**
* 常见的请求方式
*/
public void requestNormal() {
TestDemo testDemo = new TestDemo();
testDemo.requestData("requestData", new RequestCallBack() {
@Override
public void onResponse(String data) {
// fastJson 或者GSON都可以直接将json数据转化成对应的Bean对象
TestBean testBean = JSON.parseObject(data,TestBean.class);
// do other things
}
@Override
public void onError(int errorCode, String errorDesp) {
}
});
}
问题点
这样子看起来是没有问题的,但是,既然每次请求的返回结果都是json而且希望解析成Bean类以方便后续处理,而且可以用FastJson/Gson等框架直接解析成对应的BeanObject,那为什么我们不能直接统一就转了呢?还要每次在请求的地方单独去处理,太麻烦了吧,而且以后万一有需要修改的话还得每个地方去改。嗯,看到这个问题第一反应肯定是泛型。
嗯,好像是一个泛型就能搞定的事情,好像我们在RequestCallBack实现类中指定对应的Bean类,然后在onResponse的时候把Bean类传过去不就好了嘛。可是真的如此吗?按照刚刚的设想的我们改造一下:
RequestCallBack改造如下:
public abstract class RequestCallBack<T extends BaseBean>{
public T t;
/**
* 成功接收到数据被调用
* @param t 接收并且解析后到的BeanObject
*/
public abstract void onResponse(T t);
public abstract void onError(int errorCode,String errorDesp);
}
分发response改成如下格式:
/**
* 主线程分发对应的response
* @param response
*/
public void dispatchResponseOnUi(String response){
mRequestCallBack.onResponse(JSON.parseObject(response, mRequestCallBack.t.getClass()));
}
嗯,看起来是没问题了,但是,少年,你这里t是空的啊!确定不会空指针?那我们在构造的时候new一个T不就好了嘛,只能说你太天真了,IDE会提示你Type parameter “T” cannot be instantiated directly.
仔细想想这种方式肯定是行不通的嘛,你想嘛,泛型最大的好处就是在执行时才知道他具体的类型,可是你现在希望在还没执行到这里的时候就已经知道他具体的类型了,肯定是不现实的,除非通过反射根据实际的的运行环境进行实例化,可反射写起来很繁琐就算了本身执行效率也很低。那就没有其他办法了吗?
解决方案
我想到的解决方式是通过如果如下方式来解决这个问题,即可以避免每次都去解析json,又能避开反射的效率问题。
1 RequestCallBack改造如下:
public abstract class RequestCallBack {
/**
* 接收到的json数据
* @param response json数据
*/
public void onResponse(String response) {
onResponse(JSON.parseObject(response, getBeanClass()));
}
/**
* 成功接收到数据被调用
*
* @param t 接收并且解析后到的BeanObject
*/
public abstract <T extends BaseBean> void onResponse(T t);
public abstract void onError(int errorCode, String errorDesp);
/**
* 获取到希望解析成的bean类
* @param <T>
* @return
*/
public abstract <T extends BaseBean> Class<T> getBeanClass();
}
添加一个获取BeanClass的方法,另外接收到json字符串后自己内部进行解析,然后再分发给onResponse(T t)。
2 分发response的地方维持最初的不变,仍然是分发给onResponse(String response)
/**
* 主线程分发对应的response
* @param response
*/
public void dispatchResponseOnUi(String response){
mRequestCallBack.onResponse(response);
}
3 那我们看下更改后请求的写法
/**
* 优化后的请求方式
*/
public void requestNew() {
TestDemo testDemo = new TestDemo();
testDemo.requestData("requestData", new RequestCallBack() {
@Override
public <T extends BaseBean> void onResponse(T t) {
// 得到解析后的t
}
@Override
public void onError(int errorCode, String errorDesp) {
}
@Override
public Class<? extends BaseBean> getBeanClass() {
return TestBean.class;
}
});
}
好了,就这样我们既将json的解析聚合起来了,又避免了反射导致的效率问题。感谢您的阅读,希望对您有所帮助。如果有疑问欢迎沟通。