學習內容:java
1.NetWorkDispatcher網絡請求線程調度...android
2.NetWork網絡請求抽象類...apache
3.BasicNetWork網絡請求抽象類的具體實現...數組
4.NetWorkResponse接收網絡請求返回的響應...緩存
5.ResponseDelivery請求分配抽象類...服務器
6.ExecutorDelivery請求分配的具體實現類...網絡
上一章說完了緩存請求線程調度,那麼如今就說一下網絡請求線程調度是如何來完成的,網絡請求線程調度也是有一個核心過程的,從網絡上獲取數據信息的過程, 首先從網絡請求隊列取出請求,若是請求存在,那麼對請求進行相關的處理,若是沒有請求,那麼線程進入等待狀態,取出請求以後須要先對請求是否已經被中途取 消進行相關的判斷,若是已經請求已經被中途中斷,那麼結束此次的處理過程,若是沒有取消,那麼執行請求,獲取服務器的返回數據,而後對返回的響應是夠是 304響應進行相關的判斷,若是是304響應,那麼直接也結束對請求的處理。併發
304請求表示的是相同請求已經被服務器響應,而且返回了已經返回了相關的數據,因爲服務器的狀態是高併發的執行狀態,有可能在同一時間段對兩種或幾種相同的請求進行相關的處理,那麼對於這樣多種相同的請求,服務器只須要響應一次數據就能夠了,剩下的由ResponseDelivery去分發給全部與之相同的請求就能夠了...也就是說服務器對於同一時間段的多個相同的請求只須要響應一次...app
若是不是304響應,那麼表示此次請求是一個新的請求,那麼咱們須要向服務器發送請求來獲取服務器的響應,最後經過是否進行緩存進行判斷以後,一個請求就能夠被分發出去了...socket
1.NetWorkDispatcher.java
1.1 變量的定義
private final BlockingQueue<Request> mQueue; //請求隊列... /** The network interface for processing requests. */ private final Network mNetwork; //網絡請求對象,用於執行網絡請求工做 /** The cache to write to. */ private final Cache mCache; //緩存對象,用於緩存數據 /** For posting responses and errors. */ private final ResponseDelivery mDelivery; //用於分發請求... /** Used for telling us to die. */ private volatile boolean mQuit = false; // 用於關閉線程...
1.2 NetWorkDispatcher構造函數...
網絡請求線程調度對象的初始化過程...構造函數比較簡單,沒什麼說的...
public NetworkDispatcher(BlockingQueue<Request> queue, Network network, Cache cache, ResponseDelivery delivery) { mQueue = queue; mNetwork = network; mCache = cache; mDelivery = delivery; }
1.3 public void run(){}
run()方法,線程中最重要的方法,必需要繼承的...整個方式就如同剛開始介紹的同樣,從請求隊列中取出請求,而後判斷請求是否被中斷,中斷就結束處理,沒有中斷就發送請求,而後對服務器是否已經對此次請求返回了響應數據,若是服務器已經響應了與之相同的請求,那麼就中止對此次請求的處理,由分發響應函數去處理,若是是一個新的請求,那麼須要定義一個請求對象,而後發送...最後獲取到服務器響應後,對響應進行分發...
@Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //設置線程優先級... Request request; while (true) { try { // Take a request from the queue. request = mQueue.take(); //取出請求... } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take");//添加標識符.. // If the request was cancelled already, do not perform the // network request. if (request.isCanceled()) { //若是請求被中途取消... request.finish("network-discard-cancelled"); 添加標識符... continue; } // Tag the request (if API >= 14) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { //若是API>=10,那麼爲請求設置標籤... TrafficStats.setThreadStatsTag(request.getTrafficStatsTag()); } // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); //發送請求獲取服務器的響應... request.addMarker("network-http-complete"); // If the server returned 304 AND we delivered a response already, // we're done -- don't deliver a second identical response. if (networkResponse.notModified && request.hasHadResponseDelivered()) { //若是這個請求是沒有改變..說白了就是請求若是是相同的...那麼服務器就返回一次響應...其餘的經過Delivery去分發就好了... request.finish("not-modified"); continue; } // Parse the response here on the worker thread. Response<?> response = request.parseNetworkResponse(networkResponse);//若是是一個新的請求,那麼就須要新建一個請求對象... request.addMarker("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s. //若是請求容許被緩存,而且響應的數據不爲空...那麼將此次請求放入到緩存中.. if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); //放入的操做... request.addMarker("network-cache-written"); } // Post the response back. request.markDelivered(); //確認請求要被分發... mDelivery.postResponse(request, response); 發送請求... } catch (VolleyError volleyError) { parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { //若是出現了錯誤,那麼把錯誤發送... VolleyLog.e(e, "Unhandled exception %s", e.toString()); mDelivery.postError(request, new VolleyError(e)); } } } //一個錯誤處理函數,當發生解析請求或者是分發請求時出現錯誤時進行調用... private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) { error = request.parseNetworkError(error); mDelivery.postError(request, error); }
上面涉及到了執行請求...以及分發服務器響應,可是是如何實現的呢?咱們先看如何去執行一個請求...
2.NetWork.java
NetWork.java是一個抽象的接口...對外提供一個執行請求方法的接口,方便其餘類去實現...
package com.android.volley; /** * An interface for performing requests. */ public interface Network { /** * Performs the specified request. * @param request Request to process * @return A {@link NetworkResponse} with data and caching metadata; will never be null * @throws VolleyError on errors */ public NetworkResponse performRequest(Request<?> request) throws VolleyError; }
3.BasicNetWork.java
BasicNetWork是實現NetWork的具體抽象類,是如何執行請求的一個具體過程,其中內部也封裝了一些其餘方法...
3.1 變量的定義
這幾個變量的定義相對比較抽象,可是會在下面細說...
protected static final boolean DEBUG = VolleyLog.DEBUG; //用於Volley內部調試.. private static int SLOW_REQUEST_THRESHOLD_MS = 3000;//對於緩慢的請求定義了一個請求時間... private static int DEFAULT_POOL_SIZE = 4096; //Int值,用於之後的獲取網絡數據... protected final HttpStack mHttpStack; //Http請求棧... protected final ByteArrayPool mPool; //ByteArrayPool對象...
3.2 public BasicNetWork(){}
構造函數...構造函數構造了一個保存Http請求棧,以及一個獲取網絡數據對象...
public BasicNetwork(HttpStack httpStack) { // If a pool isn't passed in, then build a small default pool that will give us a lot of // benefit and not use too much memory. this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE)); //調用下面函數.. } /** * @param httpStack HTTP stack to be used * @param pool a buffer pool that improves GC performance in copy operations */ //創建一個BasicNetWork對象,這個對象保存了一個請求棧區,另外一個參數用於獲取數據而創建的對象... public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) { mHttpStack = httpStack; mPool = pool; }
3.3 public NetworkResponse performRequest(Request<?> request) throws VolleyError {}
相當重要的方法,用於執行請求,這裏咱們也能夠看到,請求傳遞的參數並無寫死,而是使用了泛型的方式造成了良好的擴展,也便是說傳遞過來的請求是什麼類型,那麼就執行什麼類型的請求...
@Override public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); //獲取請求開始的時間,用於調試... while (true) { HttpResponse httpResponse = null; //請求響應對象... byte[] responseContents = null; //請求內容對象... Map<String, String> responseHeaders = new HashMap<String, String>(); //map集合,用於保存數據報的Header中的數據.. try { // Gather headers. Map<String, String> headers = new HashMap<String, String>(); //用於保存緩存下來的Header...緩存的Header通常包含服務響應的總體時間,緩存新鮮度驗證等屬性值... addCacheHeaders(headers, request.getCacheEntry()); //添加請求頭部的過程... httpResponse = mHttpStack.performRequest(request, headers);//執行請求,獲取響應.. StatusLine statusLine = httpResponse.getStatusLine();//獲取響應狀態... int statusCode = statusLine.getStatusCode();//獲取狀態碼.. responseHeaders = convertHeaders(httpResponse.getAllHeaders());//獲取響應後的Header中的全部數據... // Handle cache validation. //對304響應的一個判斷過程,若是是304響應,那麼直接走緩存,從緩存獲取數據... if (statusCode == HttpStatus.SC_NOT_MODIFIED) { return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, request.getCacheEntry().data, responseHeaders, true); } // Some responses such as 204s do not have content. We must check. if (httpResponse.getEntity() != null) {//判斷響應是不是204判斷,因爲204響應時不返回數據信息的...所以須要判斷... responseContents = entityToBytes(httpResponse.getEntity()); //若是存在內容,那麼經過getEntity()方法獲取數據... } else { // Add 0 byte response as a way of honestly representing a // no-content request. responseContents = new byte[0]; //返回空數據... } // if the request is slow, log it. long requestLifetime = SystemClock.elapsedRealtime() - requestStart; logSlowRequests(requestLifetime, request, responseContents, statusLine);//若是一個請求的時間超過了指定的緩慢請求時間,那麼須要顯示這個時間... if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_NO_CONTENT) { throw new IOException(); //若是請求狀態出現錯誤,那麼拋出異常... } return new NetworkResponse(statusCode, responseContents, responseHeaders, false); //若是請求不是304請求,而且上面異常狀況也沒有發生,那麼返回新的請求中的內容+頭部以及狀態碼... } catch (SocketTimeoutException e) { attemptRetryOnException("socket", request, new TimeoutError()); //套接字異常.. } catch (ConnectTimeoutException e) { attemptRetryOnException("connection", request, new TimeoutError());//鏈接超時異常... } catch (MalformedURLException e) { throw new RuntimeException("Bad URL " + request.getUrl(), e);//url異常... } catch (IOException e) {//IO異常的處理... int statusCode = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { //若是響應存在,可是出現異常.. statusCode = httpResponse.getStatusLine().getStatusCode(); //返回異常狀態碼..可使客戶端知道異常狀況... } else { throw new NoConnectionError(e); } VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); if (responseContents != null) {//若是響應內容不是空... networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false);//獲取響應... //請求須要進行驗證,或者是須要受權異常處理... if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } else { // TODO: Only throw ServerError for 5xx status codes. throw new ServerError(networkResponse); } } else { throw new NetworkError(networkResponse); } } } }
這個函數涉及了其餘幾個函數...
3.4 private void logSlowRequests(long requestLifetime, Request<?> request,byte[] responseContents, StatusLine statusLine) {}
記錄一個請求——響應的時間超出了預先設置的緩慢請求時間,那麼須要進行記錄...記錄響應碼,響應內容等等函數比較的簡單...
private void logSlowRequests(long requestLifetime, Request<?> request, byte[] responseContents, StatusLine statusLine) { if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) { VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " + "[rc=%d], [retryCount=%s]", request, requestLifetime, responseContents != null ? responseContents.length : "null", statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount()); }
3.5 private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {}
添加請求的請求頭部,從緩存當中獲取頭部信息添加到此次請求...
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) { // If there's no cache entry, we're done. if (entry == null) { //緩存數據爲空...直接return return; } if (entry.etag != null) { headers.put("If-None-Match", entry.etag); //返回新鮮度驗證標誌.. } if (entry.serverDate > 0) { Date refTime = new Date(entry.serverDate); //返回請求——響應的時間... headers.put("If-Modified-Since", DateUtils.formatDate(refTime)); } }
3.6 private static void attemptRetryOnException(String logPrefix, Request<?> request,VolleyError exception) throws VolleyError {}
嘗試重試策略方法...若是請求發生了異常...
private static void attemptRetryOnException(String logPrefix, Request<?> request, VolleyError exception) throws VolleyError { RetryPolicy retryPolicy = request.getRetryPolicy(); //獲取請求的重試策略... int oldTimeout = request.getTimeoutMs(); try { retryPolicy.retry(exception); //重試方式執行... } catch (VolleyError e) { request.addMarker( String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout)); throw e; } request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout)); }
3.7 private static Map<String, String> convertHeaders(Header[] headers) {}
上面涉及到了當響應返回的時候須要獲取響應數據報的Header,將全部的Header數據獲取並保存...以鍵值對的形式保存在map集合當中...最後傳遞給responseHeaders集合...
private static Map<String, String> convertHeaders(Header[] headers) { Map<String, String> result = new HashMap<String, String>(); for (int i = 0; i < headers.length; i++) { result.put(headers[i].getName(), headers[i].getValue()); } return result; }
3.8 private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {}
當Http請求響應成功時,咱們須要從HttpEntity中獲取返回的內容數據...數據的獲取調用此函數..這個函數採用了PoolingArrayByteOutputStream()流..這個流採用了字節回收機制,能夠減小內存的分配和回收...咱們先知道就行...後面還會具體說明...
private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError { PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength()); //新建一個流對象.. byte[] buffer = null; //緩衝字節.. try { InputStream in = entity.getContent();//將Entity中保存的內容封裝成流... if (in == null) { throw new ServerError(); } buffer = mPool.getBuf(1024); //緩衝流分配...首先new一個緩衝字節數組... int count; while ((count = in.read(buffer)) != -1) { bytes.write(buffer, 0, count); //寫入數據... } return bytes.toByteArray();//返回數據.. } finally { try { // Close the InputStream and release the resources by "consuming the content". entity.consumeContent(); } catch (IOException e) { // This can happen if there was an exception above that left the entity in // an invalid state. VolleyLog.v("Error occured when calling consumingContent"); } mPool.returnBuf(buffer); bytes.close(); } }
這樣整體就完成了請求以後響應數據的獲取,也就是數據報的Header+Body的數據被保存了起來...那麼完成了數據的獲取,就須要響應數據的分發了...分發到請求才是請求——響應的一個最終完成過程...
4.NetWorkResponse.java
那麼響應的傳遞就須要經過NetWorkResponse來進行傳遞,不管是從網絡上獲取的請求數據,仍是從緩存當中獲取的請求數據,都會被封裝成NetWorkResponse,而後傳遞給相應請求中的parseNetWorkResponse方法,在進行下一步的處理...
package com.android.volley; import org.apache.http.HttpStatus; import java.util.Collections; import java.util.Map; /** * Data and headers returned from {@link Network#performRequest(Request)}. */ public class NetworkResponse { //對服務器的響應進行一個完全封裝... public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers, boolean notModified) { this.statusCode = statusCode; this.data = data; this.headers = headers; this.notModified = notModified; } //構造函數..用來調用第一個構造函數,傳遞過來的響應只包含數據部分... public NetworkResponse(byte[] data) { this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false); } //第二個構造函數,經過響應的數據和相關頭部來調用第一個構造函數... public NetworkResponse(byte[] data, Map<String, String> headers) { this(HttpStatus.SC_OK, data, headers, false); } /** The HTTP status code. */ public final int statusCode; //響應狀態碼... /** Raw data from this response. */ public final byte[] data; //響應數據... /** Response headers. */ public final Map<String, String> headers; //以鍵值對的形式保存首部.. /** True if the server returned a 304 (Not Modified). */ public final boolean notModified; //304響應的判斷... }
NetWorkResponse只是對服務器的響應的一個進一步封裝,以參數的形式傳遞到Request.parseNetWorkResponse()方法...若是中間並麼有出現什麼異常狀況,那麼最後相應實現了Request.parseNetWorkResponse類會調用Response.success方法,將此次請求進行最後的封裝,封裝成Response<T>的形式,請求是什麼類型,T就是什麼類型...
5.Response.java
package com.android.volley; public class Response<T> { /** Callback interface for delivering parsed responses. */ public interface Listener<T> { /** Called when a response is received. */ public void onResponse(T response); //當一個請求——相應成功後的監聽... } /** Callback interface for delivering error responses. */ public interface ErrorListener { /** * Callback method that an error has been occurred with the * provided error code and optional user-readable message. */ public void onErrorResponse(VolleyError error); //當請求出現了錯誤時,須要監聽錯誤.. } /** Returns a successful response containing the parsed result. */ public static <T> Response<T> success(T result, Cache.Entry cacheEntry) { return new Response<T>(result, cacheEntry); //success方法,當請求被解析成功時調用的方法... } /** * Returns a failed response containing the given error code and an optional * localized message displayed to the user. */ public static <T> Response<T> error(VolleyError error) { return new Response<T>(error); //若是解析請求時失敗須要封裝錯誤... } /** Parsed response, or null in the case of error. */ public final T result; //響應返回的結果.. /** Cache metadata for this response, or null in the case of error. */ public final Cache.Entry cacheEntry; //緩存... /** Detailed error information if <code>errorCode != OK</code>. */ public final VolleyError error; //一個錯誤對象...記錄錯誤的生成... /** True if this response was a soft-expired one and a second one MAY be coming. */ public boolean intermediate = false; //判斷一個請求是否失效... /** * Returns whether this response is considered successful. */ public boolean isSuccess() { return error == null; //若是成功就沒有錯誤傳遞... } private Response(T result, Cache.Entry cacheEntry) { //對成功時封裝的構造函數... this.result = result; this.cacheEntry = cacheEntry; this.error = null; } private Response(VolleyError error) {/失敗時封裝的構造函數... this.result = null; this.cacheEntry = null; this.error = error; } }
當響應被封裝成Response以後,就須要向客戶端發送響應了,經過postResponse方法進行發送,若是一個響應成功,咱們須要發送響應,可是若是中途出現了失敗,那麼咱們須要把錯誤發送,須要讓客戶端清楚究竟是發生了什麼錯誤,這樣在錯誤發生時提示用戶到底應該怎樣進行操做...
6.ResponseDelivery.java
解析響應以後的發送類,只是一個抽象的接口,咱們能夠人爲去重寫如何發送響應...而系統默認則採用線程池的方式對響應進行發送...
package com.android.volley; public interface ResponseDelivery { /** * Parses a response from the network or cache and delivers it. */ public void postResponse(Request<?> request, Response<?> response); //對響應進行發送... /** * Parses a response from the network or cache and delivers it. The provided * Runnable will be executed after delivery. */ public void postResponse(Request<?> request, Response<?> response, Runnable runnable); //對響應發送,同時開啓一個線程去執行其餘事情...線程在這個方法結束後執行... /** * Posts an error for the given request. */ public void postError(Request<?> request, VolleyError error); //發送錯誤信息... }
7.ExecutorDelivery.java
發送請求或者是錯誤的具體實現類,採用線程池的方式對響應進行發送...不管是服務器響應正確仍是錯誤,咱們都須要對其進行封裝發送給客戶端,讓客戶端去清楚服務器到底返回了什麼東西...
package com.android.volley; import android.os.Handler; import java.util.concurrent.Executor; public class ExecutorDelivery implements ResponseDelivery { /** Used for posting responses, typically to the main thread. */ private final Executor mResponsePoster; //定義一個線程池... //定義了一個Response的傳輸接口... public ExecutorDelivery(final Handler handler) { // Make an Executor that just wraps the handler. mResponsePoster = new Executor() { @Override public void execute(Runnable command) { handler.post(command); } }; } //構造函數,定義了一個線程池... public ExecutorDelivery(Executor executor) { mResponsePoster = executor; } //發送請求的抽象方法的實現... @Override public void postResponse(Request<?> request, Response<?> response) { postResponse(request, response, null); } /*最後都是經過調用此方法來發送請求,由於poseResponse的方法有兩種 * 一種是public void postResponse(Request<?> request, Response<?> response); * 另外一種是 public void postResponse(Request<?> request, Response<?> response, Runnable runnable); *這兩個方法上面已經說過,就是一個帶有一個附加線程,一個沒有而已... *但最終都須要調用這個方法... */ @Override public void postResponse(Request<?> request, Response<?> response, Runnable runnable) { request.markDelivered(); //表示能夠發送請求... request.addMarker("post-response"); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); //用線程封裝好Response..經過線程池的方式去管理這些線程...從這一步開始run()方法已經被調用了... } //若是出現了錯誤,那麼將錯誤封裝,同時也要發送給請求的客戶端... @Override public void postError(Request<?> request, VolleyError error) { request.addMarker("post-error"); Response<?> response = Response.error(error); //封裝錯誤.. mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null)); //發送錯誤... } @SuppressWarnings("rawtypes") private class ResponseDeliveryRunnable implements Runnable { private final Request mRequest; //請求 private final Response mResponse; //響應 private final Runnable mRunnable; //其餘線程... //構造函數很是的簡單..對三者的一個封裝過程... public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) { mRequest = request; mResponse = response; mRunnable = runnable; } //run方法... @SuppressWarnings("unchecked") @Override public void run() { // If this request has canceled, finish it and don't deliver. if (mRequest.isCanceled()) { //若是請求被中斷,那麼就不須要發送響應了... mRequest.finish("canceled-at-delivery"); return; } // Deliver a normal response or error, depending. if (mResponse.isSuccess()) { //若是服務器響應成功,中途沒有錯誤的發生,,, mRequest.deliverResponse(mResponse.result);//將服務器返回的結果發送給客戶端...這是最後的關鍵地方... } else { mRequest.deliverError(mResponse.error);//若是其中出現了失敗,須要把錯誤發送... } // If this is an intermediate response, add a marker, otherwise we're done // and the request can be finished. //這裏不知道該如何理解...翻譯成中間響應... if (mResponse.intermediate) { mRequest.addMarker("intermediate-response"); //若是是須要進行調試過程... } else { mRequest.finish("done");//若是不是表示此次請求結束... } // If we have been provided a post-delivery runnable, run it. if (mRunnable != null) { //若是附加線程不是空,那麼就啓動附加線程.. mRunnable.run(); } } } }
咱們能夠看到,最後服務器的響應被封裝以後,經過mRequest.deliveryResponse或者是mRequest.deliveryerror進行發送...而這兩個方法就會在相應類型的請其中獲得重寫...由於全部的其餘請求都是繼承Request類的...Request類中只是一個抽象的方法,具體的實如今那些實際實現了Request的類中...而每個實現類中都會去調用mListener.onResponse(response)方法,這裏只表示請求成功時調用的方法...
abstract protected void deliverResponse(T response);
public interface Listener<T> { /** Called when a response is received. */ public void onResponse(T response); }
這樣在客戶端重寫OnResponse方法以後,就完全的完成了請求——響應的結束過程...數據也就成功的從服務器經過網絡成功的發送給了客戶端...