相信大家在平时的开发中都或多或少的引用一些优秀的开源框架来提高项目的开发速度,那么在使用时,有没有想过对这些框架进行二次封装呢,假如你今天使用的是volley,明天项目要求使用retrofit或者asynchttp等其他网络框架,那么如果你没进行二次封装,那么改起来将会是很痛苦的一件事,每一个调用网络请求的地方你都需要去修改。
本文将对volley进行二次封装,如果不知道volley为何物或者如何使用的,可以先看郭霖大神博客Android Volley完全解析(一),初识Volley的基本用法再来看本文。
Ok,Let's go!
假设我们和服务端约定的返回数据格式(这个根据自身需求定制)为
{"code":"200","msg":"成功","data":{}}
如果我们每次请求都进行解析,那么会出现非常多重复的代码,而我们一般都是只关心data中的数据,那么我们的目的就是直接通过回调获得data中的数据,至于code的判断,交给二次封装中的代码。
根据上面的json格式,我们创建对应的实体类
public class HttpResult {
private Object data;
private int code;
private String msg;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
我们还需要一个泛型回调类用于回调结果,其中onSuccess是成功回调,onFail是失败回调。
public abstract class HttpCallBack<T> {
public abstract void onSuccess(T data);
public abstract void onFail(String msg);
}
其中泛型T就是json中data对应的数据,如果data中是一个User实体,那么我们回调直接写成HttpCallBack<User>,如果data中是一个User数组,那么我们回调直接写成HttpCallBack<List<User>>,再如果我们只想获得data中的json字符串,那么就是HttpCallBack<String>。是不是想想都觉得很爽,使用时自己不需要任何的解析。当然,现在只是预想,这些返回都需要我们在封装中进行判断处理。
这里我们使用Gson进行json的解析,使用Gson解析json很简单,只需要使用public <T> T fromJson(String json, Type typeOfT),其中json参数无需多说,是json字符串,typeOfT是泛型T的类型,方法返回的是泛型T,这时,我们只需要传入正确的Type,就可得到我们需要的数据然后通过回调传出去。
那么问题来了,Type如果获取呢?在我们的封装中,我们只需要获取HttpCallBack<T>中T的类型,怎么获取呢?答案是ParameterizedType,ParameterizedType 就是形如“ 类型<> ”的类型,如HttpCallBack<T>、List<T>、Map<String,User>。比如Map<String,User>,我们通过getActualTypeArguments()方法可以获得Type[],里面的内容是[String.class,User.class],数组的长度由< >中的个数决定。具体代码如下:
public static Type getTType(Class<?> clazz) {
Type mySuperClassType = clazz.getGenericSuperclass();
Type[] types = ((ParameterizedType) mySuperClassType).getActualTypeArguments();
if (types != null && types.length > 0) {
//T
return types[0];
}
return null;
}
其中class参数是HttpCallBack<T>的引用,返回值就是HttpCallBack<T>中T的type。假设我们的回调是HttpCallBack<User>,那么返回的Type就是User.class;假设我们的回调是HttpCallBack<List<User>>,那么返回的Type就是List<User>,此时再使用上面提到的Gson进行解析,就很轻松了。
接下来就是二次封装的主要代码
public class HttpUtil {
private static HttpUtil mHttpUtil;
private Gson mGson;
//请求连接的前缀
private static final String BASE_URL = "";
//连接超时时间
private static final int REQUEST_TIMEOUT_TIME = 60 * 1000;
//volley请求队列
public static RequestQueue mRequestQueue;
private HttpUtil() {
mGson = new Gson();
//这里使用Application创建全局的请求队列
mRequestQueue = Volley.newRequestQueue(MyApplication.myApplication);
}
public static HttpUtil getInstance() {
if (mHttpUtil == null) {
synchronized (HttpUtil.class) {
if (mHttpUtil == null) {
mHttpUtil = new HttpUtil();
}
}
}
return mHttpUtil;
}
/**
* http请求
*
* @param url http地址(后缀)
* @param param 参数
* @param httpCallBack 回调
*/
public <T> void request(String url, final Map<String, String> param, final HttpCallBack<T> httpCallBack) {
StringRequest stringRequest = new StringRequest(Request.Method.POST, BASE_URL + url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
if (httpCallBack == null) {
return;
}
Type type = getTType(httpCallBack.getClass());
HttpResult httpResult = mGson.fromJson(response, HttpResult.class);
if (httpResult != null) {
if (httpResult.getCode() != 200) {//失败
httpCallBack.onFail(httpResult.getMsg());
} else {//成功
//获取data对应的json字符串
String json = mGson.toJson(httpResult.getData());
if (type == String.class) {//泛型是String,返回结果json字符串
httpCallBack.onSuccess((T) json);
} else {//泛型是实体或者List<>
T t = mGson.fromJson(json, type);
httpCallBack.onSuccess(t);
}
}
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (httpCallBack == null) {
return;
}
String msg = error.getMessage();
httpCallBack.onFail(msg);
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
//请求参数
return param;
}
};
//设置请求超时和重试
stringRequest.setRetryPolicy(new DefaultRetryPolicy(REQUEST_TIMEOUT_TIME, 1, 1.0f));
//加入到请求队列
if (mRequestQueue != null)
mRequestQueue.add(stringRequest.setTag(url));
}
private Type getTType(Class<?> clazz) {
Type mySuperClassType = clazz.getGenericSuperclass();
Type[] types = ((ParameterizedType) mySuperClassType).getActualTypeArguments();
if (types != null && types.length > 0) {
//T
return types[0];
}
return null;
}
}
其中,我们规定了请求全部为post,如果项目中既有post又有get,那么可以通过传参的方式进行设置。
接下来是如何调用:
类型一:User
Map<String, String> param = new HashMap<>();
param.put("account", "zhijieeeeee");
param.put("password", "123456");
HttpUtil.getInstance().request("UserController/login", param, new HttpCallBack<User>() {
@Override
public void onSuccess(User user) {
}
@Override
public void onFail(String msg) {
}
});
类型二:List<User>
Map<String, String> param = new HashMap<>();
param.put("currentPage", "1");
HttpUtil.getInstance().request("InputController/getUserList", param, new HttpCallBack<List<User>>() {
@Override
public void onSuccess(List<User> list) {
}
@Override
public void onFail(String msg) {
}
});
类型三:String
Map<String, String> param = new HashMap<>();
param.put("currentPage", "1");
HttpUtil.getInstance().request("CropController/getCropList", param, new HttpCallBack<String>() {
@Override
public void onSuccess(String data) {
}
@Override
public void onFail(String msg) {
}
});
现在,我们二次封装全部完成了,假如明天你需要换网络请求框架,那么只需要修改HttpUtil中request方法的实现代码就行,其他调用HttpUtil.getInstance().request()的地方全部不用修改,很轻松完成了框架的切换。
这里给出依赖的库
compile 'eu.the4thfloor.volley:com.android.volley:2015.05.28'
compile 'com.google.code.gson:gson:2.2.4'