本篇源碼解析基於 Volley 1.1.1
Volley 是 Google 開發的一款網絡請求框架,目前已停止更新。雖然目前大家的關注焦點都在 Retrofit、OkHttp 等第三方網絡請求框架,團隊的項目中所用的也是這兩個框架,但 Volley 中還是有非常多優秀的設計思想值得我們去學習的。因此今天準備來學習一下 Volley 的源碼,瞭解一下它的核心設計思想。
Volley
我們先看到 Volley 的入口——Volley
類。
創建 RequestQueue
Volley 在使用之前,我們需要一個請求隊列對象 RequestQueue
,一般整個應用統一用同一個 RequestQueue
,讓我們看看創建它的方法 Volley.newRequestQueue(Context)
:
/**
* 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, (BaseHttpStack) null);
}
它轉調到了 Volley.newRequestQueue(Context, BaseHttpStack)
方法,同時還有一個 Volley.newRequestQueue(Context, HttpStack)
方法:
/**
* 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.
* @deprecated Use {@link #newRequestQueue(Context, BaseHttpStack)} instead to avoid depending
* on Apache HTTP. This method may be removed in a future release of Volley.
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
if (stack == null) {
return newRequestQueue(context, (BaseHttpStack) null);
}
return newRequestQueue(context, new BasicNetwork(stack));
}
從上面的註釋可以看出,HttpStack
是一個用於在 NetWork
中使用的對象,如果傳入的 stack
不爲 null,則會調用到 Volley.newRequestQueue(Context, Network)
,否則它同樣會轉調到 Volley.newRequestQueue(Context, BaseHttpStack)
:
/**
* 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 A {@link BaseHttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;
// 創建 NetWork 對象
if (stack == null) {
// stack 爲空則創建一個Stack,然後再創建 NetWork
// 高版本下,HttpStack 使用的是 HurlStack,低版本下使用 HttpClientStack
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
// At some point in the future we'll move our minSdkVersion past Froyo and can
// delete this fallback (along with all Apache HTTP code).
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info =
context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
network =
new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else {
network = new BasicNetwork(stack);
}
return newRequestQueue(context, network);
}
這個方法主要做的事情就是根據 stack
創建 netWork
對象。
在 stack
爲空時,對於高於 9 的 SDK 版本,使用 HurlStack
,而對於低於它的版本則使用 HttpClientStack
。根據上面的註釋可以看出這樣做的原因:因爲在 SDK 9 之前的 HttpUrlConnection
不是很可靠。(我們可以推測在高版本 (SDK > 9)Volley 基於 HttpUrlConnection
實現,低版本則基於 HttpClient
實現。
另外這裏可能會比較疑惑,NetWork
和 HttpStack
都是用來做什麼的?這些問題我們後面都會一一解決。
它最後同樣調用到了 Volley.newRequestQueue(Context, Network)
:
private static RequestQueue newRequestQueue(Context context, Network network) {
final Context appContext = context.getApplicationContext();
// Use a lazy supplier for the cache directory so that newRequestQueue() can be called on
// main thread without causing strict mode violation.
DiskBasedCache.FileSupplier cacheSupplier =
new DiskBasedCache.FileSupplier() {
private File cacheDir = null;
@Override
public File get() {
if (cacheDir == null) {
cacheDir = new File(appContext.getCacheDir(), DEFAULT_CACHE_DIR);
}
return cacheDir;
}
};
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheSupplier), network);
queue.start();
return queue;
}
首先根據 Context
獲取到了應用緩存文件夾並創建 Cache
文件。這裏有個比較小的細節,爲了在 Application 調用 newRequestQueue
同時又不被 StrictMode 有關文件操作相關的規則所影響,Volley 中使用了一個 FileSupplier
來對 File
進行包裝,它採用了一個懶創建的思路,只有用到的時候才創建對應的 cacheDir
。
之後構造了 RequestQueue
,調用了其 start
方法並對其返回。可以看出來,Volley
是一個 RequestQueue
的靜態工廠。
RequestQueue
RequestQueue
中維護了三個容器:兩個 PriorityBlockingQueue
:mCacheQueue
與 mNetQueue
,以及一個 Set
:mCurrentRequests
。
mCacheQueue
:用於存放緩存的待請求的Request
。mNetQueue
:用於存放等待發起的Request
。mCurrentQueue
:用於存放當前正在進行請求的Request
。
創建
我們先來看看 RequestQueue
的構造函數:
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
它轉調到了另一個構造函數,並傳遞了一個默認線程數 DEFAULT_NETWORK_THREAD_POOL_SIZE
,它代表了網絡請求分派線程啓動時的默認個數,默認值爲 4。
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(
cache,
network,
threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
這個構造函數創建了一個主線程的 Handler
並用它構建了一個 ExecutorDelivery
,關於 ExecutorDelivery
我們後面再討論,它是一個用於交付 Response 和 Error 信息的類。
後面轉調的構造函數主要是進行一些賦值。
啓動
接着我們看看 RequestQueue.start
,看看它是如何啓動的:
/** Starts the dispatchers in this queue. */
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();
}
}
start
的主要用途是啓動該 Queue
中的 Dispatcher
。它的主要步驟如下:
- 調用
stop
方法停止所有正在運行的Dispatcher
- 創建
CacheDispatcher
並啓動。 - 創建前面指定個數的
NetworkDispatcher
(默認爲 4 個)並啓動。
可以看出來,每個 RequestQueue
中共有 5 個 Dispatcher
,其中有 4 個 NetworkDispatcher
和 1 個 CacheDispatcher
。
入隊
我們可以通過 RequestQueue.add
將一個 Request
入隊,它會根據當前 Request
是否需要進行緩存將其加入 mNetworkQueue
或 mCacheQueue
(這裏實際上 GET
請求首先會放入 mCacheQueue
,其餘請求直接放入 mNetworkQueue
)
/**
* 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);
}
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
sendRequestEvent(request, RequestEvent.REQUEST_QUEUED);
// If the request is uncacheable, skip the cache queue and go straight to the network.
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
mCacheQueue.add(request);
return request;
}
停止
我們先看看 stop
做了什麼:
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (final NetworkDispatcher mDispatcher : mDispatchers) {
if (mDispatcher != null) {
mDispatcher.quit();
}
}
}
這裏僅僅是對每個 Dispatcher
調用了其 quit
方法。
結束
RequestQueue
還有個 finish
方法,對應了 Request.finish
:
/**
* Called from {@link Request#finish(String)}, indicating that processing of the given request
* has finished.
*/
@SuppressWarnings("unchecked") // see above note on RequestFinishedListener
<T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
sendRequestEvent(request, RequestEvent.REQUEST_FINISHED);
}
主要是將結束的 Request
從 mCurrentRequests
中移除,並調用外部註冊的回調以及發送 REQUEST_FINISHED
事件。
ExecutorDelivery
接下來我們看看 ExecutorDelivery
究竟是做什麼的
/** Delivers responses and errors. */
public class ExecutorDelivery implements ResponseDelivery {
private final Executor mResponsePoster;
/**
* 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);
}
};
}
//...
}
根據上面的註釋可以看出 ExecutorDelivery
的主要作用是交付 Response 和 Error 信息。
它內部持有了一個名爲 ResponsePoster
的 Executor
,每個調用這個 Poster
的 execute
方法的 Runnable
都會通過 Handler.post
發送到主線程的 MessageQueue
中。
接着我們看到它內部的方法:
@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));
}
可以看出來,它內部的方法主要是將 Response 信息和 Error 信息 post 到 MessageQueue
中,其中 request
和 response
會被包裝爲一個 ResponseDeliveryRunnable
。
ResponseDeliveryRunnable
ResponseDeliveryRunnable
是 ExecutorDelivery
的一個內部類,可以看到它的 run
方法:
@Override
public void run() {
// NOTE: If cancel() is called off the thread that we're currently running in (by
// default, the main thread), we cannot guarantee that deliverResponse()/deliverError()
// won't be called, since it may be canceled after we check isCanceled() but before we
// deliver the response. Apps concerned about this guarantee must either call cancel()
// from the same thread or implement their own guarantee about not invoking their
// listener after cancel() has been called.
// 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();
}
}
這裏會根據 Request
、Response
的不同狀態調用 Request
中的不同方法以對 Response 和 Error 的結果進行交付:
- 如果
Request
被取消,調用Request.finish
方法直接結束。 - 如果請求成功,調用
Request.deliverResponse
方法反饋Response
- 如果請求失敗,調用
Request.deliverError
方法反饋Error
- 如果該請求只是一箇中間請求,則不結束該請求,而是給它加上一個 “intermediate-response” 標記,否則結束該請求。
- 如果
postResponse
方法傳遞了一個Runnbale
進來,則執行該Runnable
。
NetworkDispatcher
我們接着來看到 NetworkDispatcher
,它繼承自 Thread
,我們先看到其 run
方法:
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
try {
processRequest();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
VolleyLog.e(
"Ignoring spurious interrupt of NetworkDispatcher thread; "
+ "use quit() to terminate it");
}
}
}
它在不斷循環調用 processRequest
方法:
// Extracted to its own method to ensure locals have a constrained liveness scope by the GC.
// This is needed to avoid keeping previous request references alive for an indeterminate amount
// of time. Update consumer-proguard-rules.pro when modifying this. See also
// https://github.com/google/volley/issues/114
private void processRequest() throws InterruptedException {
// Take a request from the queue.
Request<?> request = mQueue.take();
processRequest(request);
}
這裏首先從 mQuque
( RequestQueue
中的 NetQuque
)中取出了 Request
,雖然 NetworkDispacher
是多個同時執行,但由於使用了 BlockingQueue
因此不用考慮線程安全問題。
可以發現這是一種典型的生產者消費者模型,多個 NetworkDispatcher
不斷地從 NetQueue
中取出 Request
並進行網絡請求,用戶就是網絡請求的生產者,而 NetworkDispatcher
就是網絡請求的消費者。
processRequest
方法繼續調用了 processRequest(Request)
方法:
void processRequest(Request<?> request) {
long startTimeMs = SystemClock.elapsedRealtime();
// 發送 REQUEST_NETWORK_DISPATCH_STARTED 事件
request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
try {
request.addMarker("network-queue-take");
// 如果這個 request 已經被取消,則 finish 它並通知 Listener Response 沒有用
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
return;
}
addTrafficStatsTag(request);
// 調用 mNetwork.performRequest 發起同步請求拿到 Response
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果返回了 304 並且我們已經交付了 Reponse,則 finish 它並通知 Listener Response 沒有用
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
}
// 對 response 進行轉換
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 如果適用,寫入緩存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 通過 ExecutorDelivery 對 Response 進行交付並將 request 標記爲已交付
request.markDelivered();
mDelivery.postResponse(request, response);
// 通知 Listener 已獲得 Response
request.notifyListenerResponseReceived(response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
// 交付 Error 信息並通知 Listener Response 沒有用
parseAndDeliverNetworkError(request, volleyError);
request.notifyListenerResponseNotUsable();
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
// 交付 Error 信息並通知 Listener Response 沒有用
mDelivery.postError(request, volleyError);
request.notifyListenerResponseNotUsable();
} finally {
// 發送 REQUEST_NETWORK_DISPATCH_FINISHED 事件
request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_FINISHED);
}
}
上面的代碼比較長,我們可以將其分爲如下的步驟:
- 發送
REQUEST_NETWORK_DISPATCH_STARTED
事件,表示請求已開始 - 如果
request
已經被取消,則finish
它並通知 ListenerResponse
沒有用,不再繼續進行 - 調用
mNetwork.performRequest
發起同步請求拿到Response
- 如果返回了 304 並且我們已經交付了
Reponse
,則finish
它並通知 ListenerResponse
沒有用,不再繼續進行 - 對
Response
進行轉換 - 如果該
Request
可以進行緩存,以Request
爲 key,Response
爲 value 進行緩存 - 通過
ExecutorDelivery
對Response
進行交付並將Request
標記爲已交付 - 通知 Listener 已獲得
Response
- 如果出現了異常,交付 Error 信息並通知 Listener
Response
沒有用,其中所有 Error 都需要轉換成VollyError
類。 - 發送
REQUEST_NETWORK_DISPATCH_FINISHED
事件,表示請求已結束
從上面的步驟我們可以得到一些信息:
- 有一套
Event
機制用於傳送各種事件 Network
類是網絡請求真正的實現類。Response
有個轉換的問題- 存在一套
Response
緩存機制。
CacheDispatcher
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 {
processRequest();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
VolleyLog.e(
"Ignoring spurious interrupt of CacheDispatcher thread; "
+ "use quit() to terminate it");
}
}
}
它也是在不斷地調用 processRequest
方法:
private void processRequest() throws InterruptedException {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take();
processRequest(request);
}
這裏與 NetworkDispatcher
中一樣,從 mCacheQueue
中取出待請求的 Request
,之後看到 processRequest(Request)
:
void processRequest(final Request<?> request) throws InterruptedException {
request.addMarker("cache-queue-take");
// 發送 Event
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_STARTED);
try {
// 若 request 已取消,直接 finish 它
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
return;
}
// 通過 request 嘗試獲取 Entry
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
// 若緩存未命中,則將其放入 mNetworkQueue 等待進行網絡請求
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}
// 如果緩存過期,放入 mNetworkQueue 等待進行網絡請求
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}
// 緩存命中,轉換並獲取 Response
request.addMarker("cache-hit");
Response<?> response =
request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// 如果得到的 response 不需要刷新,直接交付 Response
mDelivery.postResponse(request, response);
} else {
// 如果緩存到期,則將當前 response 設爲中間 Response,進行 Response 交付
// 當 Response 成功交付,則會將其放入 mNetworkQueue 等待網絡請求
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
// 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) {
Thread.currentThread().interrupt();
}
}
});
} else {
// request has been added to list of waiting requests
// to receive the network response from the first request once it returns.
mDelivery.postResponse(request, response);
}
}
} finally {
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
}
}
代碼比較長,它主要有以下的步驟:
- 若
Request
已取消,直接finish
它。 - 通過
Request
嘗試獲取緩存的Response
對應的Entry
。 - 若緩存未命中,則將其放入
mNetworkQueue
等待進行網絡請求。 - 若緩存過期,則將其放入
mNetworkQueue
等待進行網絡請求。 - 若緩存命中,轉換並獲取
Response
。 - 若得到的
Response
不需要刷新,直接交付Response
。 - 若檢測
Response
發現緩存到期,則將當前Response
設爲中間Response
,進行Response
交付。此時傳入了一個Runnable
,當Response
成功交付時,會將Request
放入mNetworkQueue
等待網絡請求。
簡單點說就是,如果緩存命中且 Response
未過期,則直接將其交付,否則將其放入 mNetworkQueue
等待網絡請求。
Request
前面提到的很多機制都與 Request
有關,爲了更好的理解,我們暫時先不關心 Network
是如何發起網絡請求的,讓我們先看看 Request
的組成。
Request
只是一個抽象類,它的幾個實現如下:
StringRequest
:返回String
的Request
。JsonRequest
:JsonObjectRequest
和JsonArrayRequest
的基類。JsonObjectRequest
:返回JsonObject
的Request
。JsonArrayRequest
:返回JsonArray
的Request
。ImageRequest
:返回圖片的Request
。ClearCacheRequest
:一個用來清空緩存的Request
,並不是用來進行網絡請求的Request
。
構建
要構建一個 Request
,我們可以通過其構造函數,其中不同的子類有不同的構造函數,主要的不同是體現在其傳入的 Listener
的不同,例如 StringRequest
的構造函數如下:
public StringRequest(int method, String url, Listener<String> listener, @Nullable ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
它們都會調用 Request(method, url, errorListener)
的構造函數:
public Request(int method, String url, @Nullable Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}
如果想要創建一個 POST 請求並傳入參數,需要重寫它的 getParams
方法:
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
取消
取消一個 Request
十分簡單,只需要將 mCanceled
置爲 true 即可:
public void cancel() {
synchronized (mLock) {
mCanceled = true;
mErrorListener = null;
}
}
Response 轉換
Request
的 parseNetworkResponse
方法是一個抽象方法,需要不同子類去具體實現。
例如 StringRequest
的實現如下:
@Override
@SuppressWarnings("DefaultCharset")
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
// Since minSdkVersion = 8, we can't call
// new String(response.data, Charset.defaultCharset())
// So suppress the warning instead.
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
非常簡單,根據 Response.data
轉化爲了對應的 String
。
Response 的交付
Response
的交付方法 deliverResponse
是一個抽象方法,需要不同子類去具體實現,它主要做的事就是調用 Listener
的 onResponse
方法,並傳入轉換後的對象。
例如 StringRequest
的實現如下:
@Override
protected void deliverResponse(String response) {
Response.Listener<String> listener;
synchronized (mLock) {
listener = mListener;
}
if (listener != null) {
listener.onResponse(response);
}
}
Error 的交付
Error 的交付在 Request
中進行了實現,也非常簡單,實際上就是調用 ErrorListener
的 onErrorResponse
方法:
public void deliverError(VolleyError error) {
Response.ErrorListener listener;
synchronized (mLock) {
listener = mErrorListener;
}
if (listener != null) {
listener.onErrorResponse(error);
}
}
結束
/**
* Notifies the request queue that this request has finished (successfully or with error).
*
* <p>Also dumps all events from this request's event log; for debugging.
*/
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(Request.this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
}
主要是調用了 RequestQueue.finish
方法,它會將當前 Request
從正在執行的 Request
隊列移除。
緩存
在 Response
被緩存之前會調用 Request.shouldCache
判斷是否需要緩存,
/** Whether or not responses to this request should be cached. */
// TODO(#190): Turn this off by default for anything other than GET requests.
private boolean mShouldCache = true;
可以看到,除了 GET
請求,其餘請求方式都不允許緩存。
我們再看看存在緩存中的 key 是如何通過 Request
轉換而得:
public String getCacheKey() {
String url = getUrl();
// If this is a GET request, just use the URL as the key.
// For callers using DEPRECATED_GET_OR_POST, we assume the method is GET, which matches
// legacy behavior where all methods had the same cache key. We can't determine which method
// will be used because doing so requires calling getPostBody() which is expensive and may
// throw AuthFailureError.
// TODO(#190): Remove support for non-GET methods.
int method = getMethod();
if (method == Method.GET || method == Method.DEPRECATED_GET_OR_POST) {
return url;
}
return Integer.toString(method) + '-' + url;
}
由於新版 Volley 中只支持 GET
請求進行緩存,因此 Request
是以 url 作爲緩存的 key。
Event 機制
我們暫時先不關心 Network
的具體實現,讓我們先看看 Event
發出後做了什麼。
調用 sendEvent
後,轉調到了 RequestQueue.sendRequestEvent
中:
void sendRequestEvent(Request<?> request, @RequestEvent int event) {
synchronized (mEventListeners) {
for (RequestEventListener listener : mEventListeners) {
listener.onRequestEvent(request, event);
}
}
}
可以看出,我們的用戶可以向 RequestQueue
中註冊一個 RequestEventListener
來監聽 Request
相關的 Event
。
Response
相比 Request
,Response
就簡單了很多(感覺很多 Response
的功能放在了 Request
),裏面主要是攜帶了一些請求結束後的相關信息。
public class Response<T> {
/** Callback interface for delivering parsed responses. */
public interface Listener<T> {
/** Called when a response is received. */
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.
*/
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<>(result, cacheEntry);
}
/**
* 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<>(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;
}
}
Network
從前面的分析中,我們可以知道,真正的網絡請求是通過 Network
類實現的,它是一個抽象類,只有一個子類 BaseNetwork
。
我們主要關注它的 performRequest
方法:
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
// 不斷循環
HttpResponse httpResponse = null;
byte[] responseContents = null;
List<Header> responseHeaders = Collections.emptyList();
try {
// 獲取 Header
Map<String, String> additionalRequestHeaders =
getCacheHeaders(request.getCacheEntry());
// 通過 httpStack.executeRequest 來同步執行網絡請求
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
int statusCode = httpResponse.getStatusCode();
responseHeaders = httpResponse.getHeaders();
// 若沒有 Modified,則構建一個 NetworkResponse 並返回
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(
HttpURLConnection.HTTP_NOT_MODIFIED,
/* data= */ null,
/* notModified= */ true,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
}
// 將 response 的 header 和緩存的 entry 結合,變成新的 Headers
List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);
return new NetworkResponse(
HttpURLConnection.HTTP_NOT_MODIFIED,
entry.data,
/* notModified= */ true,
SystemClock.elapsedRealtime() - requestStart,
combinedHeaders);
}
// 有的 Response 沒有 content,因此需要對 content 進行判斷
InputStream inputStream = httpResponse.getContent();
if (inputStream != null) {
responseContents =
inputStreamToBytes(inputStream, httpResponse.getContentLength());
} 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, statusCode);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
// 返回請求結果構建的 Response
return new NetworkResponse(
statusCode,
responseContents,
/* notModified= */ false,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode;
if (httpResponse != null) {
statusCode = httpResponse.getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
NetworkResponse networkResponse;
if (responseContents != null) {
networkResponse =
new NetworkResponse(
statusCode,
responseContents,
/* notModified= */ false,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED
|| statusCode == HttpURLConnection.HTTP_FORBIDDEN) {
attemptRetryOnException(
"auth", request, new AuthFailureError(networkResponse));
} else if (statusCode >= 400 && statusCode <= 499) {
// Don't retry other client errors.
throw new ClientError(networkResponse);
} else if (statusCode >= 500 && statusCode <= 599) {
if (request.shouldRetryServerErrors()) {
attemptRetryOnException(
"server", request, new ServerError(networkResponse));
} else {
throw new ServerError(networkResponse);
}
} else {
// 3xx? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}
這裏的代碼非常長,主要是下面的步驟:
- 不斷循環進行請求,直到出現錯誤或請求成功
- 獲取 Headers
- 之後通過
mBaseHttpStack
進行網絡請求 - 如果請求結果沒有
Modified
,則將返回的 Headers 與緩存的信息結合構建NetworkResponse
並返回。 - 最後如果請求成功,構建
NetworkResponse
並返回。 - 若出現錯誤,如果是可以進行重試的(如 3xx 狀態碼或超時),會嘗試進行重試。
- 否則會根據具體錯誤構建對應的
Error
或Response
並返回。
HttpStack
HttpStack
是我們真正執行網絡請求的類,它有兩個實現:HurlStack
以及 HttpClientStack
,前者基於 HttpUrlConnection
實現,後者基於 HttpClient
實現。
由於 HttpClient
已經被徹底拋棄,並且目前幾乎已經不存在 SDK 9 以下的機器,因此我們只需要分析 HurlStack
即可,我們看到其 executeRequest
方法:
@Override
public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<>();
map.putAll(additionalHeaders);
// Request.getHeaders() takes precedence over the given additional (cache) headers).
map.putAll(request.getHeaders());
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
boolean keepConnectionOpen = false;
try {
for (String headerName : map.keySet()) {
connection.setRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
if (!hasResponseBody(request.getMethod(), responseCode)) {
return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()));
}
// Need to keep the connection open until the stream is consumed by the caller. Wrap the
// stream such that close() will disconnect the connection.
keepConnectionOpen = true;
return new HttpResponse(
responseCode,
convertHeaders(connection.getHeaderFields()),
connection.getContentLength(),
new UrlConnectionInputStream(connection));
} finally {
if (!keepConnectionOpen) {
connection.disconnect();
}
}
}
這裏沒什麼難度,就不再詳細介紹,主要是調用了 HttpUrlConnection
的 API 進行網絡請求。
緩存機制
Volley 的緩存機制基於 Cache
這個接口實現,它對外暴露了 put
、get
等常見的緩存操作接口。默認情況下采用基於磁盤的緩存 DiskBasedCache
。
這一塊主要是一些對文件的讀取與寫入,暫時就不研究了,有興趣的讀者可以自行閱讀。
總結
爲了在 Application 調用 newRequestQueue
同時又不被 StrictMode 有關文件操作相關的規則所影響,Volley 中使用了一個 FileSupplier
來對 File
進行包裝,它採用了一個懶創建的思路,只有用到的時候才創建對應的 cacheDir
文件
RequestQuque
維護了三個隊列,分別是待請求緩存隊列、待網絡請求隊列以及正在請求隊列。
RequestQueue
默認情況下維護了 4 個 NetworkDispatcher
以及 1 個 CacheDispatcher
,它們都是繼承自 Thread
,通過異步進行網絡請求的方式從而提高請求效率。
請求的成功或失敗都會通過 ExecutorDelivery
進行交付,然後通過 Request
通知到各個 Listener
。
存在一套緩存機制,以 Request
爲 Key,以 Response
爲 Value。只有 GET 請求需要進行緩存,所有 GET 請求會首先被放入緩存隊列 mCacheQueue
,當緩存未命中或緩存過期時,纔會被放入 mNetQueue
進行網絡請求。
Network
是一個對網絡請求的執行進行包裝的類,它主要負責了 Response
的轉換以及對重試的支持。
HttpStack
是真正執行網絡請求的類,它在高於 SDK 9 的版本下會基於 HttpUrlConnection
進行網絡請求,否則會基於 HttpClient
進行網絡請求。
Cache
實現了 Volley 的緩存機制,默認情況下采用基於磁盤的緩存 DiskBasedCache
。
Volley 雖沒有像 OkHttp 那樣靈活的攔截器機制,但提供了很多 Listener
供外界對請求過程進行監聽。