相信大家在平時的開發(fā)中都或多或少的引用一些優(yōu)秀的開源框架來提高項目的開發(fā)速度,那么在使用時,有沒有想過對這些框架進行二次封裝呢,假如你今天使用的是volley,明天項目要求使用retrofit或者asynchttp等其他網(wǎng)絡(luò)框架,那么如果你沒進行二次封裝,那么改起來將會是很痛苦的一件事,每一個調(diào)用網(wǎng)絡(luò)請求的地方你都需要去修改。
本文將對volley進行二次封裝,如果不知道volley為何物或者如何使用的,可以先看郭霖大神博客Android Volley完全解析(一),初識Volley的基本用法再來看本文。
Ok,Let's go!
假設(shè)我們和服務(wù)端約定的返回數(shù)據(jù)格式(這個根據(jù)自身需求定制)為
{"code":"200","msg":"成功","data":{}}
如果我們每次請求都進行解析,那么會出現(xiàn)非常多重復(fù)的代碼,而我們一般都是只關(guān)心data中的數(shù)據(jù),那么我們的目的就是直接通過回調(diào)獲得data中的數(shù)據(jù),至于code的判斷,交給二次封裝中的代碼。
根據(jù)上面的json格式,我們創(chuàng)建對應(yīng)的實體類
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;
}
}
我們還需要一個泛型回調(diào)類用于回調(diào)結(jié)果,其中onSuccess是成功回調(diào),onFail是失敗回調(diào)。
public abstract class HttpCallBack<T> {
public abstract void onSuccess(T data);
public abstract void onFail(String msg);
}
其中泛型T就是json中data對應(yīng)的數(shù)據(jù),如果data中是一個User實體,那么我們回調(diào)直接寫成HttpCallBack<User>,如果data中是一個User數(shù)組,那么我們回調(diào)直接寫成HttpCallBack<List<User>>,再如果我們只想獲得data中的json字符串,那么就是HttpCallBack<String>。是不是想想都覺得很爽,使用時自己不需要任何的解析。當然,現(xiàn)在只是預(yù)想,這些返回都需要我們在封裝中進行判斷處理。
這里我們使用Gson進行json的解析,使用Gson解析json很簡單,只需要使用public <T> T fromJson(String json, Type typeOfT),其中json參數(shù)無需多說,是json字符串,typeOfT是泛型T的類型,方法返回的是泛型T,這時,我們只需要傳入正確的Type,就可得到我們需要的數(shù)據(jù)然后通過回調(diào)傳出去。
那么問題來了,Type如果獲取呢?在我們的封裝中,我們只需要獲取HttpCallBack<T>中T的類型,怎么獲取呢?答案是ParameterizedType,ParameterizedType 就是形如“ 類型<> ”的類型,如HttpCallBack<T>、List<T>、Map<String,User>。比如Map<String,User>,我們通過getActualTypeArguments()方法可以獲得Type[],里面的內(nèi)容是[String.class,User.class],數(shù)組的長度由< >中的個數(shù)決定。具體代碼如下:
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參數(shù)是HttpCallBack<T>的引用,返回值就是HttpCallBack<T>中T的type。假設(shè)我們的回調(diào)是HttpCallBack<User>,那么返回的Type就是User.class;假設(shè)我們的回調(diào)是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創(chuàng)建全局的請求隊列
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 參數(shù)
* @param httpCallBack 回調(diào)
*/
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對應(yīng)的json字符串
String json = mGson.toJson(httpResult.getData());
if (type == String.class) {//泛型是String,返回結(jié)果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 {
//請求參數(shù)
return param;
}
};
//設(shè)置請求超時和重試
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;
}
}
其中,我們規(guī)定了請求全部為post,如果項目中既有post又有g(shù)et,那么可以通過傳參的方式進行設(shè)置。
接下來是如何調(diào)用:
類型一: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) {
}
});
現(xiàn)在,我們二次封裝全部完成了,假如明天你需要換網(wǎng)絡(luò)請求框架,那么只需要修改HttpUtil中request方法的實現(xiàn)代碼就行,其他調(diào)用HttpUtil.getInstance().request()的地方全部不用修改,很輕松完成了框架的切換。
這里給出依賴的庫
compile 'eu.the4thfloor.volley:com.android.volley:2015.05.28'
compile 'com.google.code.gson:gson:2.2.4'