之前开发都是用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 }
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 }
请求的生命周期加起来只有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调用了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。