1. 程式人生 > >Android網路框架Volley(實戰篇)

Android網路框架Volley(實戰篇)

之前講了ym—— Android網路框架Volley(體驗篇),大家應該瞭解了volley的使用,接下來我們要看看如何把volley使用到實戰專案裡面,我們先考慮下一些問題:

從上一篇來看 mQueue 只需要一個物件即可,new RequestQueue物件對資源一種浪費,我們應該在application,以及可以把取消請求的方法也在application進行統一管理,看以下程式碼:

[java] view plaincopyprint?
  1. package com.chronocloud.lib.base;  
  2. import android.app.Application;  
  3. import
     android.text.TextUtils;  
  4. import com.android.volley.Request;  
  5. import com.android.volley.RequestQueue;  
  6. import com.android.volley.VolleyLog;  
  7. import com.android.volley.toolbox.Volley;  
  8. publicclass ApplicationController extends Application {  
  9.     /** 
  10.      * Log or request TAG 
  11.      */
  12.     publicstaticfinal
     String TAG = "VolleyPatterns";  
  13.     /** 
  14.      * Global request queue for Volley 
  15.      */
  16.     private RequestQueue mRequestQueue;  
  17.     /** 
  18.      * A singleton instance of the application class for easy access in other 
  19.      * places 
  20.      */
  21.     privatestatic ApplicationController sInstance;  
  22.     @Override
  23.     public
    void onCreate() {  
  24.         super.onCreate();  
  25.         // initialize the singleton
  26.         sInstance = this;  
  27.     }  
  28.     /** 
  29.      * @return ApplicationController singleton instance 
  30.      */
  31.     publicstaticsynchronized ApplicationController getInstance() {  
  32.         return sInstance;  
  33.     }  
  34.     /** 
  35.      * @return The Volley Request queue, the queue will be created if it is null 
  36.      */
  37.     public RequestQueue getRequestQueue() {  
  38.         // lazy initialize the request queue, the queue instance will be
  39.         // created when it is accessed for the first time
  40.         if (mRequestQueue == null) {  
  41.             // 1
  42.             // 2
  43.             synchronized (ApplicationController.class) {  
  44.                 if (mRequestQueue == null) {  
  45.                     mRequestQueue = Volley  
  46.                             .newRequestQueue(getApplicationContext());  
  47.                 }  
  48.             }  
  49.         }  
  50.         return mRequestQueue;  
  51.     }  
  52.     /** 
  53.      * Adds the specified request to the global queue, if tag is specified then 
  54.      * it is used else Default TAG is used. 
  55.      *  
  56.      * @param req 
  57.      * @param tag 
  58.      */
  59.     public <T> void addToRequestQueue(Request<T> req, String tag) {  
  60.         // set the default tag if tag is empty
  61.         req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);  
  62.         VolleyLog.d("Adding request to queue: %s", req.getUrl());  
  63.         getRequestQueue().add(req);  
  64.     }  
  65.     /** 
  66.      * Adds the specified request to the global queue using the Default TAG. 
  67.      *  
  68.      * @param req 
  69.      * @param tag 
  70.      */
  71.     public <T> void addToRequestQueue(Request<T> req) {  
  72.         // set the default tag if tag is empty
  73.         req.setTag(TAG);  
  74.         getRequestQueue().add(req);  
  75.     }  
  76.     /** 
  77.      * Cancels all pending requests by the specified TAG, it is important to 
  78.      * specify a TAG so that the pending/ongoing requests can be cancelled. 
  79.      *  
  80.      * @param tag 
  81.      */
  82.     publicvoid cancelPendingRequests(Object tag) {  
  83.         if (mRequestQueue != null) {  
  84.             mRequestQueue.cancelAll(tag);  
  85.         }  
  86.     }  
  87. }  
接下來就是Volley雖然給我們提供了很多不同的Request(JsonObjectRequest,JsonArrayRequest,StringRequest,ImageRequest),但是還是滿足不了我們實戰中的需求,我們實戰開發中經常用到的是xml格式,Gson解析。

接下來我們來看看,如何自定義Request

XmlRequest:

[java] view plaincopyprint?
  1. publicclass XMLRequest extends Request<XmlPullParser> {  
  2.     privatefinal Listener<XmlPullParser> mListener;  
  3.     public XMLRequest(int method, String url, Listener<XmlPullParser> listener,  
  4.             ErrorListener errorListener) {  
  5.         super(method, url, errorListener);  
  6.         mListener = listener;  
  7.     }  
  8.     public XMLRequest(String url, Listener<XmlPullParser> listener, ErrorListener errorListener) {  
  9.         this(Method.GET, url, listener, errorListener);  
  10.     }  
  11.     @Override
  12.     protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {  
  13.         try {  
  14.             String xmlString = new String(response.data,  
  15.                     HttpHeaderParser.parseCharset(response.headers));  
  16.             XmlPullParserFactory factory = XmlPullParserFactory.newInstance();  
  17.             XmlPullParser xmlPullParser = factory.newPullParser();  
  18.             xmlPullParser.setInput(new StringReader(xmlString));  
  19.             return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));  
  20.         } catch (UnsupportedEncodingException e) {  
  21.             return Response.error(new ParseError(e));  
  22.         } catch (XmlPullParserException e) {  
  23.             return Response.error(new ParseError(e));  
  24.         }  
  25.     }  
  26.     @Override
  27.     protectedvoid deliverResponse(XmlPullParser response) {  
  28.         mListener.onResponse(response);  
  29.     }  
  30. }  

GsonRequest(注意需要自行匯入gson.jar): [java] view plaincopyprint?
  1. publicclass GsonRequest<T> extends Request<T> {  
  2.     privatefinal Listener<T> mListener;  
  3.     private Gson mGson;  
  4.     private Class<T> mClass;  
  5.     public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,  
  6.             ErrorListener errorListener) {  
  7.         super(method, url, errorListener);  
  8.         mGson = new Gson();  
  9.         mClass = clazz;  
  10.         mListener = listener;  
  11.     }  
  12.     public GsonRequest(String url, Class<T> clazz, Listener<T> listener,  
  13.             ErrorListener errorListener) {  
  14.         this(Method.GET, url, clazz, listener, errorListener);  
  15.     }  
  16.     @Override
  17.     protected Response<T> parseNetworkResponse(NetworkResponse response) {  
  18.         try {  
  19.             String jsonString = new String(response.data,  
  20.                     HttpHeaderParser.parseCharset(response.headers));  
  21.             return Response.success(mGson.fromJson(jsonString, mClass),  
  22.                     HttpHeaderParser.parseCacheHeaders(response));  
  23.         } catch (UnsupportedEncodingException e) {  
  24.             return Response.error(new ParseError(e));  
  25.         }  
  26.     }  
  27.     @Override
  28.     protectedvoid deliverResponse(T response) {  
  29.         mListener.onResponse(response);  
  30.     }  
  31. }  
接下只差最後一步了就是封裝它的錯誤處理,使用過volley的都知道,volley的監聽錯誤提示都是NoConnectionError。。。等等,這類的錯誤提示,顯然這不是我們想給使用者呈現的錯誤提示,因為就算提示了使用者也不明白什麼意思,所以我們還要封裝一下,能讓使用者看的更清楚的理解這些錯誤提示。ym—— Android網路框架Volley(體驗篇)我們講過每個請求都需要設定一個失敗的監聽: [java] view plaincopyprint?
  1. // 共用失敗回撥
  2. privateclass StrErrListener implements ErrorListener {  
  3.     @Override
  4.     publicvoid onErrorResponse(VolleyError arg0) {  
  5.         Toast.makeText(mContext,  
  6.                 VolleyErrorHelper.getMessage(arg0, mContext),  
  7.                 Toast.LENGTH_LONG).show();  
  8.     }  
  9. }  
以上程式碼有個VolleyError物件,我們可以從這個物件上下手: [java] view plaincopyprint?
  1. package com.example.volley;  
  2. import java.util.HashMap;  
  3. import java.util.Map;  
  4. import android.content.Context;  
  5. import com.android.volley.AuthFailureError;  
  6. import com.android.volley.NetworkError;  
  7. import com.android.volley.NetworkResponse;  
  8. import com.android.volley.NoConnectionError;  
  9. import com.android.volley.ServerError;  
  10. import com.android.volley.TimeoutError;  
  11. import com.android.volley.VolleyError;  
  12. import com.google.gson.Gson;  
  13. import com.google.gson.reflect.TypeToken;  
  14. //正如前面程式碼看到的,在建立一個請求時,需要新增一個錯誤監聽onErrorResponse。如果請求發生異常,會返回一個VolleyError例項。
  15. //以下是Volley的異常列表:
  16. //AuthFailureError:如果在做一個HTTP的身份驗證,可能會發生這個錯誤。
  17. //NetworkError:Socket關閉,伺服器宕機,DNS錯誤都會產生這個錯誤。
  18. //NoConnectionError:和NetworkError類似,這個是客戶端沒有網路連線。
  19. //ParseError:在使用JsonObjectRequest或JsonArrayRequest時,如果接收到的JSON是畸形,會產生異常。
  20. //SERVERERROR:伺服器的響應的一個錯誤,最有可能的4xx或5xx HTTP狀態程式碼。
  21. //TimeoutError:Socket超時,伺服器太忙或網路延遲會產生這個異常。預設情況下,Volley的超時時間為2.5秒。如果得到這個錯誤可以使用RetryPolicy。
  22. publicclass VolleyErrorHelper {  
  23.     /** 
  24.      * Returns appropriate message which is to be displayed to the user against 
  25.      * the specified error object. 
  26.      *  
  27.      * @param error 
  28.      * @param context 
  29.      * @return 
  30.      */
  31.     publicstatic String getMessage(Object error, Context context) {  
  32.         if (error instanceof TimeoutError) {  
  33.             return context.getResources().getString(  
  34.                     R.string.generic_server_down);  
  35.         } elseif (isServerProblem(error)) {  
  36.             return handleServerError(error, context);  
  37.         } elseif (isNetworkProblem(error)) {  
  38.             return context.getResources().getString(R.string.no_internet);  
  39.         }  
  40.         return context.getResources().getString(R.string.generic_error);  
  41.     }  
  42.     /** 
  43.      * Determines whether the error is related to network 
  44.      *  
  45.      * @param error 
  46.      * @return 
  47.      */
  48.     privatestaticboolean isNetworkProblem(Object error) {  
  49.         return (error instanceof NetworkError)  
  50.                 || (error instanceof NoConnectionError);  
  51.     }  
  52.     /** 
  53.      * Determines whether the error is related to server 
  54.      *  
  55.      * @param error 
  56.      * @return 
  57.      */
  58.     privatestaticboolean isServerProblem(Object error) {  
  59.         return (error instanceof ServerError)  
  60.                 || (error instanceof AuthFailureError);  
  61.     }  
  62.     /** 
  63.      * Handles the server error, tries to determine whether to show a stock 
  64.      * message or to show a message retrieved from the server. 
  65.      *  
  66.      * @param err 
  67.      * @param context 
  68.      * @return 
  69.      */
  70.     privatestatic String handleServerError(Object err, Context context) {  
  71.         VolleyError error = (VolleyError) err;  
  72.         NetworkResponse response = error.networkResponse;  
  73.         if (response != null) {  
  74.             switch (response.statusCode) {  
  75.             case404:  
  76.             case422:  
  77.             case401:  
  78.                 try {  
  79.                     // server might return error like this { "error":
  80.                     // "Some error occured" }
  81.                     // Use "Gson" to parse the result
  82.                     HashMap<String, String> result = new Gson().fromJson(  
  83.                             new String(response.data),  
  84.                             new TypeToken<Map<String, String>>() {  
  85.                             }.getType());  
  86.                     if (result != null && result.containsKey("error")) {  
  87.                         return result.get("error");  
  88.                     }  
  89.                 } catch (Exception e) {  
  90.                     e.printStackTrace();  
  91.                 }  
  92.                 // invalid request
  93.                 return error.getMessage();  
  94.             default:  
  95.                 return context.getResources().getString(  
  96.                         R.string.generic_server_down);  
  97.             }  
  98.         }  
  99.         return context.getResources().getString(R.string.generic_error);  
  100.     }  
  101. }  

以上程式碼中引用的xml是: [html] view plaincopyprint?
  1. <stringname="no_internet">無網路連線~!</string>
  2. <stringname="generic_server_down">連線伺服器失敗~!</string>
  3. <stringname="generic_error">網路異常,請稍後再試~!</string>
接下來,資料請求這一塊已經說完了,我們來說下圖片這一塊,我個人喜歡使用universal-image-loader而不是volley自己提供的(個人認為使用起來universal-image-loader更便捷一些)。好啦講完了,大家可以去實戰開發了~!不懂或者遇到問題的可以留言討論~!