• 简单扒一下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。

    本博客所有博文,若无专门说明皆为原创,转载请注明作者和出处!
  • 相关阅读:
    网络爬虫之Cookies解决
    高性能异步爬虫
    Python csv存储
    pandas ExcelWriter用法及代码示例
    pandas to_excel 用法详解
    pandas read_csv 与 to_csv 方法各参数详解
    pandas read_excel操作
    pandas DataFrame.groupby()所见的各种用法详解
    Pandas 中 DataFrame 基本函数整理
    Python 解析二维码 输出文本
  • 原文地址:https://www.cnblogs.com/ifinver/p/5091323.html
Copyright © 2020-2023  润新知