簡單扒一下Volley源碼,擴展Volley生命週期

  以前開發都是用xUtils的,好用又省心。並且裏面的網絡框架適合項目封裝,生命週期不少:onStart()、onCancelled()、onLoading()、onSucess()、onFailure()等等。緩存

  原生Volley的回調接口有三個:Response.Listener、Response.ErrorListener、RequestQueue.RequestFinishedListener服務器

 1 public class Response<T> {
 2 
 3     /**
 4      * Callback interface for delivering parsed responses.
 5      */
 6     public interface Listener<T> {
 7         /**
 8          * Called when a response is received.
 9          */
10         public void onResponse(T response, Map<String, String> headers);
11     }
12 
13     /**
14      * Callback interface for delivering error responses.
15      */
16     public interface ErrorListener {
17         /**
18          * Callback method that an error has been occurred with the provided
19          * error code and optional user-readable message.
20          */
21         public void onErrorResponse(VolleyError error);
22     }
23     /**其餘方法省略***/
24 }
Response.Listener&ErrorListener
 1 public class RequestQueue {
 2 
 3     /**
 4      * Callback interface for completed requests.
 5      */
 6     public static interface RequestFinishedListener<T> {
 7         /**
 8          * Called when a request has finished processing.
 9          */
10         public void onRequestFinished(Request<T> request);
11     }
12     /**其餘方法省略**/
13 }
RequestQueue.RequestFinishedListener

  請求的生命週期加起來只有onResponse、onErrorResponse、onRequestFinished三個,明顯不夠用的,實際開發中須要本身封一些方法出來才能方便項目使用。restful

  見過一些擴展Volley生命週期的方式,大多都拖垮了Volley的性能。網絡

1、app

  閒話少說,擴展Volley生命週期的第一步是結合當前項目,明確本身的需求,須要什麼方法出來爲我所用。我當前項目的數據交互格式是請求的數據包裹在data屬性中,相似這樣:框架

1 public class Result {
2     public int error_code;
3     public String data;
4     public String error_message;
5 }

data屬性包含了須要的數據,須要把data屬性轉換爲特定的業務Bean(BO)對象,也就是說拿到服務器返回的數據時須要作兩次解析轉換。相信大部分項目都是這樣的。其實這是不符合restful接口規範的,restful接口規範是業務bean對象和Result類是繼承關係,而不是包含關係。ide

那麼接口回調大體能夠肯定以下:oop

 1 public interface RequestListener<T> {
 2 
 3     void onSuccess(T t);
 4 
 5     void onStart();
 6 
 7     void onFail(String msg);
 8 
 9     /**
10      * 任何狀況下都會被調用
11      */
12     void onFinish();
13 
14     /**
15      * 返回true表明攔截Result,不讓父類進行後續處理,而且OnSuccess不會被調用
16      * 好比經過判斷result.error_code發現有錯誤,就返回true,不用再把data字段繼續解析成特定的Bean對象了
17      *
18      * @param Result 你的項目的基礎BO
19      * @return true 是攔截剩餘生命週期方法(onSuccess和onFail)的調用,注意onFinish任何狀況下都會被調用
20      */
21     boolean onHandleRequestResult(Result result);
22 
23     /**
24      * 發生全局錯誤
25      * @param errorCode 全局錯誤碼
26      */
27     void onGlobalError(int errorCode);
28 
29     void onUpdateProgress(boolean isUpload, long current, long total);
30 }

 2、post

先看Volley怎麼實現生命週期回調的。性能

以StringRequest爲例,構造時須要傳入Response.Listener,而後被StringRequest存爲成員屬性,在deliverResponse方法中被調用onResponse()通知外部:

 1 public class StringRequest extends Request<String> {
 2 
 3     private final Response.Listener<String> mListener;
 4 
 5     public StringRequest(int method, String url, Response.Listener<String> listener,
 6                          Response.ErrorListener errorListener) {
 7         super(method, url, errorListener);
 8         mListener = listener;
 9     }
10 
11     @Override
12     protected void deliverResponse(String response) {
13         mListener.onResponse(response);
14     }
15     @Override
16     protected Response<String> parseNetworkResponse(NetworkResponse response) {
17         /**略**/
18     }
19     /**其餘方法省略**/
20 }

 

這裏就實現了咱們須要的onSucess()方法了,繼續深究這個deliverResponse()方法。

Volley的RequestQueue內部有N+1個工做Thread=N個網絡訪問調度器(NetworkDispatcher)+1個緩存調度器(CacheDispatcher),先無論緩存的使用,來看NetworkDispatcher線程,它的run()方法中有個死循環不停地從阻塞式的請求隊列BlockingQueue<Request<?>>中取出Request執行網絡訪問

 

 1 public void run() {
 2         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 3         Request<?> request;
 4         while (true) {
 5             long startTimeMs = SystemClock.elapsedRealtime();
 6             // release previous request object to avoid leaking request object when mQueue is drained.
 7             request = null;
 8             try {
 9                 // Take a request from the queue.
10                 request = mQueue.take();
11             } catch (InterruptedException e) {
12                 // We may have been interrupted because it was time to quit.
13                 if (mQuit) {
14                     return;
15                 }
16                 continue;
17             }
18 
19             try {
20                 request.addMarker("network-queue-take");
21 
22                 // If the request was cancelled already, do not perform the
23                 // network request.
24                 if (request.isCanceled()) {
25                     request.finish("network-discard-cancelled");
26                     continue;
27                 }
28 
29                 addTrafficStatsTag(request);
30 
31                 // Perform the network request.
32                 NetworkResponse networkResponse = mNetwork.performRequest(request);
33                 request.addMarker("network-http-complete");
34 
35                 // If the server returned 304 AND we delivered a response already,
36                 // we're done -- don't deliver a second identical response.
37                 if (networkResponse.notModified && request.hasHadResponseDelivered()) {
38                     request.finish("not-modified");
39                     continue;
40                 }
41 
42                 // Parse the response here on the worker thread.
43                 Response<?> response = request.parseNetworkResponse(networkResponse);
44                 request.addMarker("network-parse-complete");
45 
46                 // Write to cache if applicable.
47                 // TODO: Only update cache metadata instead of entire record for 304s.
48                 if (request.shouldCache() && response.cacheEntry != null) {
49                     mCache.put(request.getCacheKey(), response.cacheEntry);
50                     request.addMarker("network-cache-written");
51                 }
52 
53                 // Post the response back.
54                 request.markDelivered();
55                 mDelivery.postResponse(request, response);
56             } catch (VolleyError volleyError) {
57                 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
58                 parseAndDeliverNetworkError(request, volleyError);
59             } catch (Exception e) {
60                 VolleyLog.e(e, "Unhandled exception %s", e.toString());
61                 VolleyError volleyError = new VolleyError(e);
62                 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
63                 mDelivery.postError(request, volleyError);
64             }
65         }
66     }
NetworkDispatcher#run()

 

在經過訪問網絡取得服務器返回的數據後,NetworkDispatcher調用了Request的parseNetworkResponse()方法,拿到parseNetworkResponse方法的返回值,而後下面mDelivery.postResponse(request, response);這步代碼的做用是調用Request的deliverResponse()。

接着分析這個mDelivery,它的實例對象是ExecutorDelivery,在RequestQueue的構造方法中實例化,而後在start方法中構造NetworkDispatcher時將對象引用傳遞進去(Java中並無引用傳遞,這麼說習慣了哈哈)。

 1 public class RequestQueue {
 2 
 3     public RequestQueue(Cache cache, Network network, int threadPoolSize) {
 4         this(cache, network, threadPoolSize,
 5                 new ExecutorDelivery(new Handler(Looper.getMainLooper())));
 6     }
 7 
 8     public RequestQueue(Cache cache, Network network, int threadPoolSize,
 9                         ResponseDelivery delivery) {
10         mCache = cache;
11         mNetwork = network;
12         mDispatchers = new NetworkDispatcher[threadPoolSize];
13         mDelivery = delivery;
14     }
15 
16     public void start() {
17         stop();  // Make sure any currently running dispatchers are stopped.
18         // Create the cache dispatcher and start it.
19         mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
20         mCacheDispatcher.start();
21 
22         // Create network dispatchers (and corresponding threads) up to the pool size.
23         for (int i = 0; i < mDispatchers.length; i++) {
24             NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
25                     mCache, mDelivery);
26             mDispatchers[i] = networkDispatcher;
27             networkDispatcher.start();
28         }
29     }
30     /**其餘方法、屬性省略**/
31 }

來看ExecutorDelivery的postResponse方法

 1 public class ExecutorDelivery implements ResponseDelivery {
 2     /**
 3      * 理解爲線程池,能夠execute傳入的Runnable
 4      */
 5     private final Executor mResponsePoster;
 6 
 7     @Override
 8     public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
 9         request.markDelivered();
10         request.addMarker("post-response");
11         mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
12     }
13 
14     private class ResponseDeliveryRunnable implements Runnable {
15         /**
16          * 省略構造方法和屬性
17          **/
18         public void run() {
19             /**省略其餘步驟**/
20             if (mResponse.isSuccess()) {
21  mRequest.deliverResponse(mResponse.result);
22             } else {
23                 mRequest.deliverError(mResponse.error);
24             }
25             /**省略其餘步驟**/
26         }
27     }
28     /**省略其餘方法**/
29 }

可見NetworkDispatcher中的這行mDelivery.postResponse(request, response) 就是調用Request的deliverResponse()方法。

好如今回到NetworkDispatcher的run方法中,先是調用了StringRequest的parseNetworkResponse,而後又調用了StringRequest的deliverResponse()方法。

核心方法被發現了,就是這個run()方法。它先從請求隊列中(1)取出一個請求,(2)去請求網絡數據,(3)成功或失敗,(4)而後調用Request的方法把原始數據解析成須要的業務對象,(5)緩存解析的業務對象,(6)把業務對象傳遞給Request的deliverResponse方法。這6個步驟就是每一個Request的生命週期,也是咱們擴展Volley生命週期的地方。以下

 1 public void run() {
 2         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 3         while (true) {
 4             long startTimeMs = SystemClock.elapsedRealtime();
 5             Request<?> request;
 6             try {
 7                 // Take a request from the queue.
 8                 request = mQueue.take();
 9             } catch (InterruptedException e) {
10                 // We may have been interrupted because it was time to quit.
11                 if (mQuit) {
12                     return;
13                 }
14                 continue;
15             }
16 
17             try {
18                 request.addMarker("network-queue-take");
19 
20                 if(!request.isStarted()) {
21                     mDelivery.postStart(request);
22                 }
23                 // If the request was cancelled already, do not perform the
24                 // network request.
25                 if (request.isCanceled()) {
26                     request.finish("network-discard-cancelled");
27                     continue;
28                 }
29 
30                 addTrafficStatsTag(request);
31 
32                 // Perform the network request.
33                 NetworkResponse networkResponse = mNetwork.performRequest(request);
34                 request.addMarker("network-http-complete");
35 
36                 // If the server returned 304 AND we delivered a response already,
37                 // we're done -- don't deliver a second identical response.
38                 if (networkResponse.notModified && request.hasHadResponseDelivered()) {
39                     request.finish("not-modified");
40                     continue;
41                 }
42 
43                 // Parse the response here on the worker thread.
44                 Response<?> response = request.parseNetworkResponse(networkResponse);
45                 request.addMarker("network-parse-complete");
46 
47                 // Write to cache if applicable.
48                 // TODO: Only update cache metadata instead of entire record for 304s.
49                 if (request.shouldCache() && response.cacheEntry != null) {
50                     mCache.put(request.getCacheKey(), response.cacheEntry);
51                     request.addMarker("network-cache-written");
52                 }
53 
54                 // Post the response back.
55                 request.markDelivered();
56                 mDelivery.postResponse(request, response);
57             }catch (CanceledError canceledError) {
58                 request.finish("network-discard-cancelled2");
59             }catch (VolleyError volleyError) {
60                 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
61                 parseAndDeliverNetworkError(request, volleyError);
62             } catch (Exception e) {
63                 VolleyLog.e(e, "Unhandled exception %s", e.toString());
64                 VolleyError volleyError = new VolleyError(e);
65                 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
66                 mDelivery.postError(request, volleyError);
67             }
68         }
69     }

把Request擴展出一個onStart()方法,這個mDelivery.postStart()方法參照原生的postDelivery()實現。

 1 public class ExecutorDelivery implements ResponseDelivery {
 2     /**
 3      * Used for posting responses, typically to the main thread.
 4      */
 5     private final Executor mResponsePoster;
 6 
 7 
 8     @Override
 9     public void postStart(Request<?> request) {
10         mResponsePoster.execute(new RequestStartRunnable(request));
11     }
12 
13     private class RequestStartRunnable implements Runnable {
14 
15         private Request mRequest;
16 
17         public RequestStartRunnable(Request request) {
18             mRequest = request;
19         }
20 
21         @Override
22         public void run() {
23             if (mRequest.isCanceled()) {
24                 return;
25             }
26             if (mRequest.isStarted())
27                 return;
28  mRequest.markStarted();
29  mRequest.onStart();
30         }
31     }
32 }

同時還要在ExecutorDelivery中本身擴展一個postFinish,以完成onFinish方法。看上面NetworkDispatcher#run()方法中有屢次調用Request.finish(...),而後查看各處代碼能夠發現Request的finish方法在各類狀況下都會被volley調用(是否是必定會被Volley調用另說,有其餘手段來保證必定會被調用)。而後就能夠在Request#finish()方法中擴展出一個onFinish()方法。

 1 void finish(final String tag) {
 2         mBody = null;
 3         if (mRequestQueue != null) {
 4             mRequestQueue.finish(this);
 5         }
 6         if (!isFinished) {
 7             mResponseDelivery.postFinish(this);
 8         }
 9 
10         if (VolleyLog.MarkerLog.ENABLED) {
11             final long threadId = Thread.currentThread().getId();
12             if (Looper.myLooper() != Looper.getMainLooper()) {
13                 // If we finish marking off of the main thread, we need to
14                 // actually do it on the main thread to ensure correct ordering.
15                 Handler mainThread = new Handler(Looper.getMainLooper());
16                 mainThread.post(new Runnable() {
17                     @Override
18                     public void run() {
19                         mEventLog.add(tag, threadId);
20                         mEventLog.finish(this.toString());
21                     }
22                 });
23                 return;
24             }
25 
26             mEventLog.add(tag, threadId);
27             mEventLog.finish(this.toString());
28         }
29     }

這樣,Request就多出了onStart、onFinish兩個方法。下一步就是定製本身項目特有的Request了

3、

自定義的這個Request,要想StringRequest同樣,能夠傳入第一步中的RequestListener做爲回調給外部的方法。

  1 public class DIYRequest<T> extends Request<Result> {
  2 
  3     private RequestListener<T> mListener;
  4 
  5     public DIYRequest(int method, String url, RequestParams params, RequestListener<T> listener) {
  6         super(method, url, null);
  7         mListener = listener;
  8         setRetryPolicy(new DefaultRetryPolicy(
  9                 10000,
 10                 DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
 11                 DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
 12     }
 13 
 14     @Override
 15     protected void onFinish() {
 16         if (mListener != null) {
 17             mListener.onFinish();
 18         }
 19     }
 20 
 21     @Override
 22     protected void onStart() {
 23         if (mListener != null)
 24             mListener.onStart();
 25     }
 26 
 27     @Override
 28     public void deliverError(VolleyError error) {
 29         super.deliverError(error);
 30         if (error.networkResponse == null) {
 31             if (mListener != null)
 32                 mListener.onFail("網絡超時");
 33             return;
 34         }else  35             mListener.onFail("服務器出錯了,程序猿們正在奮力搶救");
 36     }
 37 
 38     @Override
 39     protected void deliverProgress(boolean isUpload, long current, long total) {
 40         super.deliverProgress(isUpload, current, total);
 41         if (mListener != null)
 42             mListener.onUpdateProgress(isUpload, current, total);
 43     }
 44 
 45     /**
 46      * 本方法運行在子線程
 47      *
 48      * @param response Response from the network
 49      * @return 你的項目的基礎BO
 50      */
 51     @Override
 52     protected Response<Result> parseNetworkResponse(NetworkResponse response) {
 53         String result;
 54         try {
 55             result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
 56         } catch (UnsupportedEncodingException e) {
 57             result = new String(response.data);
 58         }
 59         Result resultBO = 把result解析成Result(resul);
 60         return Response.success(resultBO, HttpHeaderParser.parseCacheHeaders(response));
 61     }
 62 
 63     /**
 64      * 運行在主線程,分發錯誤緣由,而後進行組裝具體BO
 65      *
 66      * @param result The parsed response returned by
 67      */
 68     @Override
 69     protected void deliverResponse(Result result) {
 70         if (result.error_code != 0) {
 71             //根據本身的項目需求判斷哪裏出錯了
 72             mListener.onGlobalError(...);
 73 
 74         } else {
 75             if (某些業務) {
 76                 mListener.onFail("用戶名或密碼錯誤");
 77             } else {
 78                 boolean goonParse = true;
 79 
 80                 //這裏實現  回調方法的onHandleRequestResult 的返回值爲true時,攔截下一步的解析操做
 81                 if (mListener != null && mListener.onHandleRequestResult(result)) {
 82                     goonParse = false;
 83                 }
 84 
 85                 if (goonParse) {
 86                     //這裏就要把result的data屬性轉換爲對應的業務bean對象了
 87                     //這裏提供一種能夠把須要的業務bean對象經過泛型傳進來的一種方法
 88                     //因此構造時傳進來的RequestListener須要帶個泛型
 89                     Type type = mListener.getClass().getGenericSuperclass();
 90                     ParameterizedType p = (ParameterizedType) type;
 91                     Type[] actualTypeArguments = p.getActualTypeArguments();
 92                     if (actualTypeArguments.length != 0) {
 93                         Type type1 = actualTypeArguments[0];
 94                         T t = JSON.parseObject(result.data, type1);
 95                         if (t == null) {
 96                             mListener.onFail("服務器返回數據錯誤");
 97                             L.i("服務器返回數據錯誤 url = %s", getUrl());
 98                         } else {
 99                             mListener.onSuccess(t);
100                         }
101                     }
102                 }
103             }
104         }
105     }
106 }

 大體就是這樣了,爲了確保Request的onFinish必定會調用,要在deliverResponse中的最後加一個步驟postFinish().

本身動手時要注意本文並無說明緩存調度器(CacheDispatcher)的改造,不要忘記了。

時間有限,有空了再把項目中的Volley抽出來。xUtils3已經出了,底層協議換成了HttpUrlConnection。

相關文章
相關標籤/搜索