android網路框架的封裝——volley
本文主要用程式碼給出如何對volley進行封裝制定出自己的request,實現專案工程中自己的網路請求框架,主要參考的是郭大神的部落格http://blog.csdn.net/guolin_blog/article/details/17612763
首先給出自定義的Request類:
public class MyVolleyRequest<T> extends Request<T> { private Gson mGson; private Class<T> mClass; private final Response.Listener<T> mListener; private RequestParams parameters; private String command; private String mRequestBody; private static final String PROTOCOL_CHARSET = "utf-8"; private static final String PROTOCOL_CONTENT_TYPE =String.format("application/json; charset=%s", PROTOCOL_CHARSET); public MyVolleyRequest(int method, Class<T> clazz, String url,RequestParams requestParams, final MyRequestListener<T> listener) { super(method, UrlConfig.SERVICE_URL, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { listener.onFailure(error); } }); mGson=new Gson(); mClass=clazz; parameters=requestParams; command=url; generateParams(); mListener=new Response.Listener<T>() { @Override public void onResponse(T response) { listener.onSuccess(response); } }; } public MyVolleyRequest(Class<T> clazz,String url,RequestParams requestParams,MyRequestListener<T> listener){ this(Method.POST,clazz,url,requestParams,listener); } private void generateParams() { Map<String, Object> paramsMap = new HashMap<String, Object>(); paramsMap.put("command", command); paramsMap.put("param", parameters.getUrlParams()); mRequestBody = mGson.toJson(paramsMap); } @Override protected Response parseNetworkResponse(NetworkResponse response) { try { String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); return Response.success(mGson.fromJson(jsonString, mClass), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } } @Override public String getBodyContentType() { return PROTOCOL_CONTENT_TYPE; } @Override public byte[] getBody() throws AuthFailureError { try { return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET); } catch (UnsupportedEncodingException uee) { VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", mRequestBody, PROTOCOL_CHARSET); return null; } } @Override protected void deliverResponse(T response) { mListener.onResponse(response); } }
該自定義request類與郭大神給出的demo區別是新加入了傳遞引數到伺服器的方法,以及合併回撥介面,改變傳遞引數等變化。該類有兩個構造方法,第二個是過載的構造方法,呼叫第一個構造方法,傳遞了post方法引數,以及url,傳遞的引數以及回撥介面。在第一個建構函式裡面通過UrlConfig.SERVICE_URL向父類傳遞url地址,而構造方法裡面的url其實是總體url的一部分,主要是用來區分不同的伺服器介面,這樣伺服器端組合出來的url才是一個請求的真正完全地址。接著通過generateParams方法獲取要提交給伺服器的引數,然後通過重寫getBody方法將引數設定進去。parseNetworkResponse方法用來解析伺服器返回的資料,通過deliverResponse方法分發出去,最後在mListener.onResponse(response)的接口裡面回撥,從而實現一次網路請求處理。
配合該類的實現,需要定義一個介面類如下:
public interface MyRequestListener<T> {
public void onSuccess(T response);
public void onFailure(VolleyError error);
}
之所以要寫一個這個介面類,就是為了把Response的Listener和ErrorListener兩個介面合併一下,省得還要在傳遞引數的時候寫兩個。
還需要一個引數類
public class RequestParams { protected final static String LOG_TAG = "RequestParams"; protected final ConcurrentHashMap<String, Object> urlParams = new ConcurrentHashMap<String, Object>(); public RequestParams() { this((Map<String, Object>) null); } public RequestParams(Map<String, Object> source) { if (source != null) { for (Map.Entry<String, Object> entry : source.entrySet()) { put(entry.getKey(), entry.getValue()); } } } public void put(String key, String value) { if (key != null && value != null) { // 防止注入攻擊過濾掉特殊字元 value = value.replace("<", "").replace(">", "").replace("'", "") .replace("/", "").replace("%", ""); urlParams.put(key, value); } } public void put(String key, Object value) { if (value != null) urlParams.put(key, value); } public void put(String key, int value) { if (key != null) { urlParams.put(key, String.valueOf(value)); } } public void put(String key, long value) { if (key != null) { urlParams.put(key, String.valueOf(value)); } } public void put(String key, double value) { if (key != null) { urlParams.put(key, String.valueOf(value)); } } @Override public String toString() { StringBuilder result = new StringBuilder(); for (ConcurrentHashMap.Entry<String, Object> entry : urlParams .entrySet()) { if (result.length() > 0) result.append("&"); result.append(entry.getKey()); result.append("="); result.append(entry.getValue()); } return result.toString(); } public ConcurrentHashMap<String, Object> getUrlParams() { return urlParams; } }
接著封裝一個公共類
public class HttpUtil {
private static final String TAG="httpUtil";
public static Request jsonRequest(Class<?> clazz, String url, RequestParams requestParams, MyRequestListener<?> listener){
MyVolleyRequest volleyRequest=new MyVolleyRequest(clazz,url,requestParams,listener);
volleyRequest.setShouldCache(false);
volleyRequest.setRetryPolicy(new DefaultRetryPolicy(300000,//預設超時時間,應設定一個稍微大點兒的
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,//預設最大嘗試次數
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
return AppContext.getInstance().getmRequestQueue().add(volleyRequest);
}
public static void showErrorToast(Context ctx, VolleyError error) {
String msg = "";
if (error instanceof TimeoutError) {
msg = "操作超時!";
} else if (error instanceof ServerError) {
msg = "伺服器異常!";
} else if (error instanceof NoConnectionError) {
msg = "無法連線到洗洗伺服器!";
} else if (error instanceof NetworkError) {
msg = "網路異常!";
} else {
msg = "操作失敗!";
}
ApplicationUtil.showToast(ctx,msg);
Log.d(TAG, error.getMessage() + "");
}
}
公共類裡面主要是呼叫的時候建立好自己的request的時候,設定一下請求引數,然後加入請求佇列即可,請求佇列最好是要在全域性的application裡面獲取,自定義的application如下:
public class AppContext extends Application {
private static AppContext Instance;
private RequestQueue mRequestQueue;
@Override
public void onCreate() {
super.onCreate();
Instance=this;
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
public static AppContext getInstance(){
return AppContext.Instance;
}
public RequestQueue getmRequestQueue(){
return mRequestQueue;
}
}
這樣使用自定義的application時要在manifest裡面加上android:name=".application.AppContext"才行。
最後使用程式碼如下:
RequestParams requestLogin = new RequestParams();
requestLogin.put("account", "admin");
requestLogin.put("password", "123456");
HttpUtil.jsonRequest(SimpleResponse.class, "10001000",requestLogin,new MyRequestListener<SimpleResponse>() {
@Override
public void onSuccess(SimpleResponse response) {
String str=response.toString();
}
@Override
public void onFailure(VolleyError error) {
Log.d("tag",error.toString()+"");
}
});
這裡定義一個SimpleResponse類來解析結果就好了,一開始封裝的MyVolleyRequest類裡面在具體應用中還可以優化,比如在程式碼
mListener=new Response.Listener<T>() {
@Override
public void onResponse(T response) {
listener.onSuccess(response);
}
};
裡面從總體上進行條件篩選,可以在解析結果的時候先不要組裝成具體的類,把jsonString先分發出去,然後定義一個基本解析類,然後在上面的回撥方法裡面先使用基本解析類解析結果進行初期判斷,這樣會省去在每一個回撥方法裡面進行重複判斷。基礎解析過後再用具體的類解析回撥到每一個請求接口裡面就好了。當然還有很多其他方法可以進行封裝和優化,大家可以一起探討。