人人都說Volley寫的非常優秀,今天我們就打開volley的源碼,來看看volley是怎么實現網絡請求的,首先,我們從剛開始使用入手
~~~
mRequestQueue = Volley.newRequestQueue(App.getInstance());
~~~
跟進代碼,Volley.newRequestQueue,
~~~
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
// 緩存目錄
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
// 拼裝UA
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
// 如果HttpStack為空
if (stack == null) {
// 判斷sdk版本
// HurlStack和HttpClientStack內部分別使用HttpUrlConnection和HttpClient
// 進行網絡請求
if (Build.VERSION.SDK_INT >= 9) {
// 使用HttpUrlConnection
stack = new HurlStack();
} else {
// 使用HttpClient
// 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 network = new BasicNetwork(stack);
// 初始化請求隊列,注意:**這里并不是一個線程**,并啟動它
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
~~~
這里面初始化了HttpStack, NetWork, RequestQueue, 并startRequestQueue,需要注意的是:**RequestQueue并不是一個線程**
進入RequestQueue.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都quit, 然后新建CacheDispatcher并啟動,這里是一個線程,內部是一個死循環,然后創建幾個(默認4個)NetworkDispatcher,并全部啟動,內部同樣是死循環。
CacheDispatcher.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.
// 如果請求被cancel了,結束請求
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
// 根據緩存的請求的key試圖從本地緩存中獲取緩存的http
Cache.Entry entry = mCache.get(request.getCacheKey());
// 如果獲取到的本地緩存是null
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.
// 到這里,就是已經本地緩存的http可用
request.addMarker("cache-hit");
// 將數據解析成Response mark
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
// 如果數據不需要重新獲取
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
// 直接回調到我們設置的Listener mark
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.
// 到這里說明我們緩存的http需要刷新
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// 這里將結果回調并且又將請求放到請求隊列中 mark
// 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;
}
}
}
~~~
這里面的內容雖然多,但是邏輯簡單,就是從緩存的請求中獲取一個請求,并判斷本地是否存在緩存的http并且判斷是否過期,根據判斷的狀態來決定是否將請求放到網絡請求隊列中等待請求的發起,還是直接從本地緩存的數據中直接獲取數據。這里面幾個mark的地方,我們過會再看,接下來我們來看看NetworkDispatcher
NetworkDispatcher.run
~~~
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request request;
while (true) { // 也是一個死循環
try {
// Take a request from the queue.
// 從請求隊列中獲取一個請求
// 如果請求是一個緩存的請求,
// 則在CacheDispatche通過一系列判斷將請求放入網絡請求隊列
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.
// 請求canceled
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) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
// Perform the network request.
// 通過NetWork.performRequest來真正的請求網絡
// 并將分析后的結果封裝到networkResponse中返回
// 這里面包含了statusCode,data,headers,notModified
// mark
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()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
// 這里將返回的結果根據我們使用的不同的request進行解析
// 解析成我們想要的數據(例如JSONObjectRequest是把結果解析成JSONObject)
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();
// 將結果投遞到我們的Listener mark
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));
}
}
}
~~~
現在我們整個流程走通了,但是還是迷迷糊糊的,為什么呢?因為我們還有幾個細節的地方沒有去看,仔細看注釋的話,很多地方我都打了mark標記,接下來我們就一塊來看看這些mark的地方實現的細節。
CacheDispatcher.run
~~~
// 將數據解析成Response mark
Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
~~~
這里是將數據解析成Response,唉?怎么一下到解析數據了,請求網絡還沒有呢? 仔細看,這里是緩存的數據。Request是一個接口,我們來到一個它的實現類JSONObjectRequest中來看看,
~~~
...
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
~~~
這里首先將response.data這個byte數組按照http頭信息中的charset構造成一個String,然后返回Response.success(),參數是我們new的一個JSONObject,為什么是JSONObject? 別忘了這里是JSONObjectRequest, 回想一下,我們使用JSONObjectRequest的時候,onResponse中是不是直接返回了一個JSONObject。
繼續看看Response.success,
~~~
/**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);
}
~~~
用我們傳進來的值構造了一個Response對象,看看Response的構造方法,
~~~
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;
}
~~~
簡答的將數據保存了一下,那我們在使用的時候怎么獲取到的數據呢?
~~~
new Response.Listener() {
public void onResponse();
}
~~~
Response肯定有一個Listener和ErrorListener的接口,
~~~
/**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);
}
~~~
至于何時去回調的接口,我們接著看下一個mark的地方
~~~
// 直接回調到我們設置的Listener mark
mDelivery.postResponse(request, response);
~~~
ResponseDelivery是一個接口,我們去看他的實現類——ExecutorDelivery
這個類的構造有一個handler的參數,
~~~
/**
* Creates a new response delivery interface.
* @param handler {@link Handler} to post responses on
*/
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);
}
};
}
~~~
在哪初始化的呢?在我們初始化RequestQueue的時候其實是去調用了另外一個RequestQueue的構造,
~~~
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
~~~
看最后一個參數,就是要找的ResponseDelivery,在new的時候我們是給它了一個handler,該handler指定了Looper使用UI線程上的Looper。
繼續看ExecutorDelivery,
~~~
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);
}
};
}
~~~
構造上就做了一件事,new了一個Executor,并重寫了execute方法,在這里面我們使用UI線程上的handler post了一個Runnable,post到哪了?肯定是到UI線程了!也就是說command的run方法里面的內容是在UI線程中執行的,我們嚴重懷疑,回調的工作是在這干的!
繼續看看我們關心的postResponse方法,
~~~
@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));
}
~~~
標記了一個request,并且post了new ResponseDeliveryRunnable()【這里前面剛說了】,
ResponseDeliveryRunnable是ExecutorDelivery的一個內部類,來看看干嘛了,
~~~
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;
}
@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();
}
}
}
~~~
繼承了實現了Runnable接口,并且有三個參數,這三個參數從哪來我們是非常關心的,來捋捋吧,
~~~
@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));
}
~~~
看來是我們從CacheDispatcher傳遞過來的,
~~~
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
mDelivery.postResponse(request, response);
} else {
...
}
~~~
這里我們調用了兩個參數的那個方法,所以Runnable為null,requst和response哪來的呢?
~~~
final Request request = mCacheQueue.take();
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
~~~
看到了嗎,熟悉的代碼,這里的代碼我們已經分析過來,request是我們從隊列中獲取的,response是我們包裝后的結果,至于怎么包裝的還急得嗎?
~~~
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
~~~
又回來了,終于找到了參數的來源了,好,那我們繼續分析代碼,在ResponseDeliveryRunnable.run中有這么幾行代碼,
~~~
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
~~~
這里是在UI線程中執行了,利用我們的request去deliverResponse,
~~~
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
~~~
哈哈,終于看見曙光了,只需要關心mListener是不是我們寫的那個參數就ok啦,
~~~
public JsonRequest(String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
}
public JsonRequest(int method, String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
mRequestBody = requestBody;
}
~~~
果然是!到現在為止,一個請求如果加入到緩存隊列,接著從緩存隊列中加入到請求隊列,判斷該請求是否有對應的本地緩存,包裝請求結果,然后各種調用,到最后回調到我們的listener都已經捋清楚了,唯一還沒看的就是網絡請求部分了,我們繼續找mark的地方,
~~~
// 這里將結果回調并且又將請求放到請求隊列中 mark
// 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.
}
}
});
~~~
這里執行了那個三個構造的ResponseDeliveryRunnable,mRunnable肯定不為空,所以
~~~
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
~~~
得以執行,也就是說我們重新將request加入到了請求隊列中,繼續看mark的地方,
NetworkDispatcher.run中,還記得這里干了嘛嗎? 這里是不斷從mNetworkQueue隊列中獲取一個request,并且執行,來看我們mark的地方,
~~~
// Perform the network request.
// 通過NetWork.performRequest來真正的請求網絡
// 并將分析后的結果封裝到networkResponse中返回
// 這里面包含了statusCode,data,headers,notModified
// mark
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
~~~
NetWork是一個接口,我們來看它的實現類——BasicNetwork,也就是我們在剛開始看到new的那個,BasicNetwork.performRequest
~~~
@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>();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
// 從Cache中獲取header,并添加到Map中
addCacheHeaders(headers, request.getCacheEntry());
// 執行網絡請求
httpResponse = mHttpStack.performRequest(request, headers);
// 獲取狀態
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
// 將header放到上面定義的responseHeaders中
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
// 內容沒有修改
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
// 這里構造了NetworkResponse
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry().data, responseHeaders, true);
}
// 將返回的內容轉化為byte數組
responseContents = entityToBytes(httpResponse.getEntity());
// 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();
}
// 構造NetworkResponse并返回
// 這里面包含狀態嗎, 返回的內容, header
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} 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);
} catch (IOException e) {
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);
}
}
}
}
~~~
基本的流程看我寫的注釋,重要的看這里的代碼,
~~~
...
httpResponse = mHttpStack.performRequest(request, headers);
...
~~~
還記得HttpStack是什么嗎?來回憶一下吧,Volley.newRequestQueue中
~~~
...
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));
}
}
...
~~~
通過判斷SDK的版本來選擇使用HttpUrlConnection還是HttpClient,我們來看看HttpClientStack也就是使用HttpClient怎么實現的,
~~~
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
// 構造請求方式
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
// 添加從緩存中獲取的header
addHeaders(httpRequest, additionalHeaders);
// 添加我們重寫getHeaders中自定義的header
addHeaders(httpRequest, request.getHeaders());
// nothing
onPrepareRequest(httpRequest);
// 獲取我們重寫的getParams方法中的參數
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
// 執行網絡請求并返回結果
return mClient.execute(httpRequest);
}
~~~
第一行代碼createHttpRequest其實就是根據我們使用的請求方式(GET,POST,PUT…)來構造不同的請求類(HttpGet, HttpPost, HttpPut),接下來是想請求中添加header,添加了兩次,第一次是從緩存中獲取的header,第二次獲取的我們重寫`getHeaders`方法中返回的那個map,接下來onPrepareRequest是一個空方法,繼續代碼,是調用我們重寫getParams獲取參數,最后執行HttpClient.execute(HttpUriRequest request)執行網絡請求,并返回結果。
這樣我們終于把Volley整個的請求過程走通了,但是還有一個問題? 我們目前為止看到的RequestQueue和CacheQueue都是空的,并沒有往里添加request,那request是什么時候添加的呢?還記得我們怎么往volley添加一個請求嗎?
~~~
mRequestQueue.add(request);
~~~
我們來看看這個add方法,
~~~
public Request 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);
}
// 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();
// 如果有相同的請求正在等待
// 將這個請求放到這個具有相同cacheKey的隊列中
// 這個cacheKey其實就是我們訪問的url,
// 也就是具有相同url的請求我們放一塊
if (mWaitingRequests.containsKey(cacheKey)) {
// 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);
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.
// 如果沒有,則添加一個null
// 并放到cache隊列中
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
~~~
為什么要這么麻煩呢? 好幾個隊列,有點暈了,我們來看看RequestQueue的finish方法,這個方式是在請求結束后調用的,上面的代碼中,很多地方地方都調用了Request.finish(tag)方法,
~~~
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());
}
}
}
~~~
這里面調用了RequestQueue的finish方法,并將當前request對象傳遞過去,
~~~
void finish(Request request) {
// Remove from the set of requests currently being processed.
// 從當前將要執行的request隊列中移除
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey;
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);
}
}
}
}
~~~
這里面將request從mCurrentRequests中移除,并且判斷waitingRequests是否含有該url的請求,如果有,則移除,并且將移除的隊列全部放到mCacheQueue,為什么要這么干?還記得我們在CacheDispatcher中那一系列判斷嗎?如果該請求的url已經緩存很有可能直接將結果回調了。這種做法解決了一個很重要的問題,我們連續訪問兩次同一個url,真正去訪問網絡的其實就一個,第二次直接從緩存中獲取結果了。
ok,到目前為止Volley的過程我們就分析完畢了。