以前開發都是用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三個,明顯不夠用的,實際開發中須要本身封一些方法出來才能方便項目使用。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調用了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。