简单扒一下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三个,明显不够用的,实际开发中需要自己封一些方法出来才能方便项目使用。

  见过一些扩展Volley生命周期的方式,大多都拖垮了Volley的性能。

一、

  闲话少说,扩展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类是继承关系,而不是包含关系。

那么接口回调大致可以确定如下:

 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 }

 二、

先看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了

三、

自定义的这个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。

你可能感兴趣的:(简单扒一下Volley源码,扩展Volley生命周期)