編輯:關於Android編程
在Android應用開發:網絡工具——Volley(一)中結合Cloudant服務介紹了Volley的一般用法,其中包含了兩種請求類型StringRequest和JsonObjectRequest。一般的請求任務相信都可以通過他們完成了,不過在千變萬化的網絡編程中,我們還是希望能夠對請求類型、過程等步驟進行完全的把控,本文就從Volley源碼角度來分析一下,一個網絡請求在Volley中是如何運作的,也可以看作網絡請求在Volley中的生命周期。
在使用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為緩存隊列處理器,創建伊始就被責令開始工作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 */ publicRequest add(Request 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 > stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList >(); } 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。
在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(); } }
public NetworkDispatcher(BlockingQueuemQueue即為mNetworkQueue,這與CacheDispatcher中使用到的是同一個。而mNetwork默認是BasicNetwork,mCache為緩存,mDelivery為最終的消息配發者,之後會分析到。接下來看其復寫的run()方法:> queue, Network network, Cache cache, ResponseDelivery delivery) { mQueue = queue; mNetwork = network; mCache = cache; mDelivery = delivery; }
@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, MapNetworkResponse保存了請求的回應數據,包括數據本身和頭,還有狀態碼以及其他相關信息。根據請求類型的不同,對回應數據的處理方式也各有不同,例如回應是String和Json的區別。所以自然而然的網絡請求類型需要對它獲得的回應數據自行處理,也就觸發了Request子類的parseNetworkResponse方法,下邊以StringRequest為例進行分析: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. emptyMap(), false); } public NetworkResponse(byte[] data, Map 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 headers; /** True if the server returned a 304 (Not Modified). */ public final boolean notModified; }
@Override protected ResponseStringRequest中對於回應首先嘗試解析數據和辨別頭數據編碼類型,若失敗則只解析數據部分。最終都是觸發Request的success方法,參數中還使用Volley自帶的HttpHeaderParser對頭信息進行了解析。需要看一下Response的success方法究竟做了什麼,鑒於Response類總共沒有多少代碼,就全部拿出來做分析了: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)); }
public class Response這就是網絡響應的類,很簡單,成功或錯誤都會直接進行標記,通過isSuccess接口提供外部查詢。如果響應成功,則消息保存在result中,解析頭信息得到的緩存數據保存在cacheEntry中。{ /** 處理解析過的回應信息的回調接口 */ public interface Listener { /** 當接收到回應後 */ public void onResponse(T response); } /** 處理錯誤回應的回調接口 */ public interface ErrorListener { /** * 錯誤發生時的回調接口 */ public void onErrorResponse(VolleyError error); } /** 返回一個包含已解析結果的成功回應 */ public static Response success(T result, Cache.Entry cacheEntry) { return new Response (result, cacheEntry); } /** * 返回錯誤回應,包含錯誤碼以及可能的其他消息 */ public static Response error(VolleyError error) { return new Response (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; } }
Request作為基類,Volley自帶的又代表性的其擴展類又StringRequest和JsonObjectRequest,如果開發者有比較大的自定義需求就需要繼承Request復寫內部一些重要的方法。同時mDelivery出場的機會這麼多,為什麼他總出現在處理請求的地方呢?下邊就對它和Request一起進行分析,其中Request依然以StringRequest為例。
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當查詢到網絡回應數據不成功時候將觸發Request的deliverError方法,這個方法StringRequest並沒有復寫,所以追溯到其父類Request中:listener, ErrorListener errorListener) { super(method, url, errorListener); mListener = listener; } public StringRequest(String url, Listener listener, ErrorListener errorListener) { this(Method.GET, url, listener, errorListener); }
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()); } } }
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網絡請求在Volley中的來龍去脈。> 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處理 } } } }
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方法的復寫,此方法對請求之後的命運有決定性的作用。
注解在android程序中的使用 何為注解: 在Java當中,注解又叫做“元數據”,它為我們在源代碼中添加信息提供了一種形式化的方法,讓我們能在以後的某個時間方便的使用這
在Android開發中,我們經常會遇到這樣一種情況:在UI界面上進行某項操作後要執行一段很耗時的代碼,比如我們在界面上點擊了一個”下載“按鈕,那麼我們需要執行網絡請求,這
Android開發通用的工具類在開發中有些代碼都是重復性的,如果能把這些代碼集中的分類提取出來(比如網絡連接、數據保存等),然後再以後寫帶碼中,直接把這些代碼復制過來,然
有些時候,自己要在布局文件中重復書寫大量的代碼來定義一個布局。這是最基本的使用,當然要掌握;但是有些場景都去對應的布局裡面寫對應的屬性,就顯得很無力。會發現,系統自帶的控