引言
在Android應用開發:網路工具——Volley(一)中結合Cloudant服務介紹了Volley的一般使用方法。當中包括了兩種請求型別StringRequest和JsonObjectRequest。一般的請求任務相信都能夠通過他們完畢了,只是在千變萬化的網路程式設計中,我們還是希望能夠對請求型別、過程等步驟進行全然的把控。本文就從Volley原始碼角度來分析一下。一個網路請求在Volley中是怎樣運作的。也能夠看作網路請求在Volley中的生命週期。
源頭RequestQueue
在使用Volley前,必須有一個網路請求佇列來承載請求,所以先分析一下這個請求佇列是怎樣申請,假設運作的。
在Volley.java中:
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
通常使用的是第二個介面。也就是僅僅有一個引數的newRequestQueue(Context context),使stack默覺得null。能夠看到我們得到的RequestQueue是通過RequestQueue申請。然後又呼叫了其start方法,最後返回給我們的。接下來看一下RequestQueue的構造方法:
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
RequestQueue有三種構造方法,通過newRequestQueue(Context context)呼叫的是最後一種。建立了一個工作池,預設承載網路執行緒數量為4個。而後兩種構造方法都會呼叫到第一個,進行了一些區域性變數的賦值。並沒有什麼須要多說的,接下來看start()方法:
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
首先進行了stop操作,將所有的執行者所有退出,從而確保當前沒有不論什麼正在工作的執行者。然後基本的工作就是開啟一個CacheDispatcher和符合執行緒池數量的NetworkDispatcher。首先分析CacheDispatcher。
CacheDispatcher快取操作
CacheDispatcher為快取佇列處理器,建立伊始就被責令開始工作start(),由於CacheDispatcher繼承於Thread類,所以須要看一下它所複寫的run方法:
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
mCache.initialize(); //初始化一個快取
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take(); //在快取序列中獲取請求,堵塞操作
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled()) { //若該請求已經被取消了。則直接跳過
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey()); //嘗試在快取中查詢是否有快取資料
if (entry == null) {
request.addMarker("cache-miss"); //若沒有則快取丟失,證明這個請求並沒有獲得實施過,扔進網路請求佇列中
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
if (entry.isExpired()) { //若請求已經過期,那麼就要去獲取最新的訊息,所以依舊丟進網路請求佇列中
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");
Response<?
> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); //請求有快取資料且沒有過期。那麼能夠進行解析,交給請求的parseNetworkReponse方法進行解析,這種方法我們能夠在自己定義個Request中進行復寫自己定義 request.addMarker("cache-hit-parsed"); if (!entry.refreshNeeded()) { //假設請求有效且並不須要重新整理,則丟進Delivery中處理。終於會觸發如StringRequest這種請求子類的onResponse或onErrorResponse // Completely unexpired cache hit. Just deliver the response. mDelivery.postResponse(request, response); } else { //請求有效,可是須要進行重新整理。那麼須要丟進網路請求佇列中 // Soft-expired cache hit. We can deliver the cached response, // but we need to also send the request to the network for // refreshing. request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry); // Mark the response as intermediate. response.intermediate = true; // Post the intermediate response back to the user and have // the delivery then forward the request along to the network. mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(request); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } } }
CacheDispatcher做了非常多事情。之後再來慢慢的消化他們。如今先看一下我們的請求通過add之後到了哪裡去。檢視RequestQueue.java的add方法:
/**
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
public <T> Request<T> add(Request<T> request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request); //增加到當前的佇列中,是一個HashSet
}
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.若這個請求不須要被快取,須要直接做網路請求,那麼就直接加到網路請求佇列中
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey(); // Volley中使用請求的URL作為儲存的key
if (mWaitingRequests.containsKey(cacheKey)) { //若等待的請求中有與所請求的URL同樣的請求,則須要做層級處理
// There is already a request in flight. Queue up.
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?
>>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); //若與已有的請求URL同樣,則建立一個層級列表儲存他們,然後再放入等待請求列表中 if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { // Insert 'null' queue for this cacheKey, indicating there is now a request in // flight. mWaitingRequests.put(cacheKey, null); //若是一個全新的請求。則直接放入等待佇列中,注意資料為null。僅僅有多個url產生層級關係了才有資料 mCacheQueue.add(request); //放入快取佇列中。快取佇列會對請求做處理 } return request; } }
這裡的mCacheQueue就是放入CacheDispatcher的那個堵塞佇列,所以在add中加入到mCacheQueue後。由於CacheDispatcher已經執行起來了,所以CacheDispatcher會對剛剛加入的網路請求做處理。分析到這裡。能夠進行一下階段性的梳理:
1. 我們的請求在增加到RequestQueue後,首先會增加到事實上體類的mCurrentRequests列表中做本地管理
2. 假設之前已經存在了和本次請求相同URL的請求,那麼會將層級關係儲存在mWaitingRequests中,若沒有則層級關係為null,相同也會儲存在mWaitingRequests中
3. 對於沒有層級關係(新的URL)的網路請求會直接放入mCacheQueue中讓CacheDispatcher對其進行處理
分析到這裡發現對於同一個URL的請求處理比較特殊。當第一次做某個網路請求A時候。A會直接放入快取佇列中由CacheDispatcher進行處理。下一次進行同一個URL的請求B時,若此時A還存在於mWaitingRequests佇列中則B的請求被雪藏,不放入mCacheQueue快取佇列進行處理,僅僅是等待。那麼等待到什麼時候呢?不難猜想到是須要等待A的請求完成後才幹夠進行B的請求。
歸結究竟就是須要知道mWaitingRequest是怎樣運作的?什麼時候儲存在當中的層級結構才會被拿出來進行請求。臨時記下這個問題,如今回頭再去繼續分析CacheDispatcher。CacheDispatcher對請求的處理能夠歸結為下面幾種情況:
1. 對於取消的請求。直接表示為完畢並跳過;
2. 對於尚未有應答資料的、資料過期、有明顯標示須要重新整理的請求直接丟入mNetworkQueue,mNetworkQueue同mCacheQueue一樣,是一個堵塞佇列;
3. 對於有應答資料且資料尚未過期的請求會出發Request的parseNetworkResponse方法進行資料解析,這種方法能夠通過繼承Request類進行復寫(定製)。
4. 對於有效應答(不管是否須要更新)都會用mDelivery進行應答,須要重新整理的請求則會再次放入到mNetworkQueue中去。
對於(1)暫不做分析。後邊會遇到。下邊分析一下mNetworkQueue的運作原理,mNetworkQueue是在CacheDispatcher構造時傳入的引數,通過RequestQueue的start()方法不難分析出相相應的處理器為NetworkDispatcher。
NetworkDispatcher網路處理
在RequestQueue的start()方法中。NetworkDispatcher存在多個,其數量等於RequestQueue構造時候傳入的網路處理執行緒數量相等。默覺得4個。
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
每個dispatcher被創造後都及時進行了start()操作,而NetworkDispatcher也是繼承於Thread的類,那麼之後須要分析其複寫的run方法。在這之前先看一下它的構造方法:
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
mQueue即為mNetworkQueue,這與CacheDispatcher中使用到的是同一個。而mNetwork預設是BasicNetwork。mCache為快取,mDelivery為終於的訊息配發者。之後會分析到。接下來看其複寫的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(); //mQueue即為mNetworkQueue,從mNetworkQueue中獲取請求,也就是說CacheDispatcher丟過來的請求是從這裡被NetworkDispatcher獲取到的。注意這裡獲取請求是堵塞的。
} catch (InterruptedException e) { //退出操作,NetworkDispatcher被設定成退出時候發出中斷請求 // 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; } addTrafficStatsTag(request); // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); //使用BasicNetwork處理請求 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()) { 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); //若產生Volley錯誤則會觸發Request的parseNetworkError方法以及mDelivery的postError方法 } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); mDelivery.postError(request, new VolleyError(e)); //對於未知錯誤,僅僅會觸發mDelivery的postError方法。
} } }
mNetwork.performRequest是真正的網路請求實施的地方,這裡對BasicNetwork不做分析。網路請求的回應是NetworkResponse型別,看一下這個型別是怎麼樣的:
/**
* Data and headers returned from {@link Network#performRequest(Request)}.
*/
public class NetworkResponse {
/**
* Creates a new network response.
* @param statusCode the HTTP status code
* @param data Response body
* @param headers Headers returned with this response, or null for none
* @param notModified True if the server returned a 304 and the data was already in cache
*/
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;
}
NetworkResponse儲存了請求的回應資料,包含資料本身和頭,還有狀態碼以及其它相關資訊。依據請求型別的不同,對回應資料的處理方式也各有不同。比如回應是String和Json的差別。所以自然而然的網路請求型別須要對它獲得的回應資料自行處理,也就觸發了Request子類的parseNetworkResponse方法。下邊以StringRequest為例進行分析:
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
StringRequest中對於回應首先嚐試解析資料和辨別頭資料編碼型別,若失敗則僅僅解析資料部分。終於都是觸發Request的success方法,引數中還使用Volley自帶的HttpHeaderParser對頭資訊進行了解析。須要看一下Response的success方法到底做了什麼,鑑於Response類總共沒有多少程式碼。就所有拿出來做分析了:
public class Response<T> {
/** 處理解析過的回應資訊的回撥介面 */
public interface Listener<T> {
/** 當接收到回應後 */
public void onResponse(T response);
}
/** 處理錯誤回應的回撥介面 */
public interface ErrorListener {
/**
* 發生錯誤時的回撥介面
*/
public void onErrorResponse(VolleyError error);
}
/** 返回一個包括已解析結果的成功回應 */
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
return new Response<T>(result, cacheEntry);
}
/**
* 返回錯誤回應,包括錯誤碼以及可能的其它訊息
*/
public static <T> Response<T> error(VolleyError error) {
return new Response<T>(error);
}
/** 解析過的響應資訊,錯誤時為null */
public final T result;
/** 響應的快取資料。錯誤時為null */
public final Cache.Entry cacheEntry;
/** 具體的錯誤資訊 */
public final VolleyError error;
/** 此回應軟體希望得到第二次回應則為true,即須要重新整理 */
public boolean intermediate = false;
/**
* 返回true代表回應成功。沒有錯誤。有錯誤則為false
*/
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;
}
}
這就是網路響應的類,非常easy。成功或錯誤都會直接進行標記,通過isSuccess介面提供外部查詢。假設響應成功,則訊息儲存在result中。解析頭資訊得到的快取資料儲存在cacheEntry中。
Request作為基類,Volley自帶的又代表性的其擴充套件類又StringRequest和JsonObjectRequest,假設開發人員有比較大的自己定義需求就須要繼承Request複寫內部一些重要的方法。
同一時候mDelivery出場的機會這麼多。為什麼他總出如今處理請求的地方呢?下邊就對它和Request一起進行分析。當中Request依舊以StringRequest為例。
ExecutorDelivery訊息分發者與Request請求
mDelivery型別為ResponseDelivery,實為介面型別:
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); }
三個介面當中兩個是回應網路應答的,最後一個回應網路錯誤。追溯RequestQueue構造的時候,預設的分發者為ExecutorDelivery:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
可見。訊息分發者工作在主執行緒上。常見的分發者所做的工作有:
@Override
public void postResponse(Request<?> request, Response<?> response) { //發出響應
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) { //發出響應
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) { //發出錯誤響應
request.addMarker("post-error");
Response<?
> response = Response.error(error); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null)); }
這裡發現一個問題,事實上在NetworkDispatcher中的request.markDelivered()是多餘的,在postResponse中已經執行了。不管是正常的響應還是錯誤都會執行ResponseDeliveryRunnable: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; //若指定了runnable。如上面分析的在網路請求有效可是須要更新的時候會指定一個runnable的
}
@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) { //假設指定了額外的runnable這裡還會對它進行執行
mRunnable.run();
}
}
}
Delivery作為網路回應的分發、處理者,對回應資料進行了最後一層的把關。而當Delivery查詢回應是否成功時,由於Request已經對回應資訊做過處理(檢查其成功還是錯誤),所以能夠查詢到正確的狀態。
若查詢到回應成功則會觸發Request的deliverResponse方法(以StringRequest為例):
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
事實上就是觸發了使用者自己定義的網路響應監聽器,mListener在StringRequest的構造中進行賦值:
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
當查詢到網路回應資料不成功時候將觸發Request的deliverError方法,這種方法StringRequest並沒有複寫,所以追溯到其父類Request中:
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
這裡mErrorListener也是使用者在使用Volley時候自定的錯誤監聽器。在StringRequest中並沒有處理,是通過super執行Request的構造方法進行賦值的:
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}
當這個請求已經完整的確定完畢後,Delivery會通知Request進行結束操作——finish:
void finish(final String tag) {
if (mRequestQueue != null) { //若請求佇列有效,則在請求佇列中標記當前請求為結束
mRequestQueue.finish(this);
} //之後都是日誌相關。不做分析
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
if (Looper.myLooper() != Looper.getMainLooper()) {
// If we finish marking off of the main thread, we need to
// actually do it on the main thread to ensure correct ordering.
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
} else {
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("%d ms: %s", requestTime, this.toString());
}
}
}
mRequestQueue為RequestQueue型別,在開篇中就分析了RequestQueue。相關的另一個問題當時沒有進行挖掘,即mWaitingQueue中保留的同樣URL的多個請求層級何時才可以被觸發。下邊分析mRequestQueue的finish方法就能解開這個疑問了:
void finish(Request<?
> request) { // Remove from the set of requests currently being processed. synchronized (mCurrentRequests) { mCurrentRequests.remove(request); //當請求已完畢。會從mCurrentRequests佇列中被移除掉 } if (request.shouldCache()) { //預設是true的。除非你呼叫Request的setShouldCache方法主動設定 synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); //獲取cacheKey,前邊說過就是URL Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey); //移除列表中的這個請求,同一時候取出其可能存在的層級關係 if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", waitingRequests.size(), cacheKey); } // Process all queued up requests. They won't be considered as in flight, but // that's not a problem as the cache has been primed by 'request'. mCacheQueue.addAll(waitingRequests); //若真的有層級關係。那麼將其它的請求所有增加到mCacheQueue中交由CacheDispatcher處理 } } } }
好了,終於待定的問題也攻克了,這就是一個Request網路請求在Volley中的來龍去脈。總結
1. 當一個RequestQueue被成功申請後會開啟一個CacheDispatcher(快取排程器)和4個(預設)NetworkDispatcher(網路請求排程器)。
2. CacheDispatcher快取排程器最為第一層緩衝。開始工作後堵塞的從快取序列mCacheQueue中取得請求:
a. 對於已經取消了的請求。直接標記為跳過並結束這個請求
b. 全新或過期的請求。直接丟入mNetworkQueue中交由N個NetworkDispatcher進行處理
c. 已獲得快取資訊(網路應答)卻沒有過期的請求。交由Request的parseNetworkResponse進行解析,從而確定此應答是否成功。
然後將請求和應答交由Delivery分發者進行處理,假設須要更新快取那麼該請求還會被放入mNetworkQueue中
3. 使用者將請求Request add到RequestQueue之後:
a. 對於不須要快取的請求(須要額外設定,預設是須要快取)直接丟入mNetworkQueue交由N個NetworkDispatcher處理。
b. 對於須要快取的。全新的請求增加到mCacheQueue中給CacheDispatcher處理
c. 須要快取,可是快取列表中已經存在了同樣URL的請求,放在mWaitingQueue中做臨時雪藏,待之前的請求完成後。再又一次加入到mCacheQueue中;
4. 網路請求排程器NetworkDispatcher作為網路請求真實發生的地方。對訊息交給BasicNetwork進行處理,相同的,請求和結果都交由Delivery分發者進行處理;
5. Delivery分發者實際上已經是對網路請求處理的最後一層了。在Delivery對請求處理之前,Request已經對網路應答進行過解析,此時應答成功與否已經設定。而後Delivery依據請求所獲得的應答情況做不同處理:
a. 若應答成功,則觸發deliverResponse方法,終於會觸發開發人員為Request設定的Listener
b. 若應答失敗,則觸發deliverError方法,終於會觸發開發人員為Request設定的ErrorListener
處理完後。一個Request的生命週期就結束了。Delivery會呼叫Request的finish操作,將其從mRequestQueue中移除,與此同一時候,假設等待列表中存在同樣URL的請求。則會將剩餘的層級請求所有丟入mCacheQueue交由CacheDispatcher進行處理。
一個Request的生命週期:
1. 通過add增加mRequestQueue中,等待請求被執行。
2. 請求執行後,呼叫自身的parseNetworkResponse對網路應答進行處理,並推斷這個應答是否成功;
3. 若成功,則終於會觸發自身被開發人員設定的Listener;若失敗。終於會觸發自身被開發人員設定的ErrorListener。
至此Volley中網路請求的來龍去脈分析清楚了。假設我們由於一些原因須要繼承Request來自己定義自己的Request,最須要注意的就是parseNetworkResponse方法的複寫。此方法對請求之後的命運有決定性的作用。