你好,我是 N0tExpectErr0r,一名熱愛技術的 Android 開發html
個人我的博客:blog.N0tExpectErr0r.cnandroid
本篇源碼解析基於 Volley 1.1.1git
Volley 是 Google 開發的一款網絡請求框架,目前已中止更新。雖然目前你們的關注焦點都在 Retrofit、OkHttp 等第三方網絡請求框架,團隊的項目中所用的也是這兩個框架,但 Volley 中仍是有很是多優秀的設計思想值得咱們去學習的。所以今天準備來學習一下 Volley 的源碼,瞭解一下它的核心設計思想。github
咱們先看到 Volley 的入口——Volley
類。緩存
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)
方法:bash
/**
* 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
對象。app
在 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
中維護了三個容器:兩個 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
到底是作什麼的
/** 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
是 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
postResponse
方法傳遞了一個 Runnbale
進來,則執行該 Runnable
。咱們接着來看到 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
它並通知 Listener Response
沒有用,再也不繼續進行mNetwork.performRequest
發起同步請求拿到 Response
Reponse
,則 finish
它並通知 Listener Response
沒有用,再也不繼續進行Response
進行轉換Request
能夠進行緩存,以 Request
爲 key,Response
爲 value 進行緩存ExecutorDelivery
對 Response
進行交付並將 Request
標記爲已交付Response
Response
沒有用,其中全部 Error 都須要轉換成 VollyError
類。REQUEST_NETWORK_DISPATCH_FINISHED
事件,表示請求已結束從上面的步驟咱們能夠獲得一些信息:
Event
機制用於傳送各類事件Network
類是網絡請求真正的實現類。Response
有個轉換的問題Response
緩存機制。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
有關,爲了更好的理解,咱們暫時先不關心 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;
}
}
複製代碼
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
的交付方法 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 的交付在 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。
咱們暫時先不關心 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
。
相比 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
類實現的,它是一個抽象類,只有一個子類 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());
}
}
}
}
複製代碼
這裏的代碼很是長,主要是下面的步驟:
mBaseHttpStack
進行網絡請求Modified
,則將返回的 Headers 與緩存的信息結合構建 NetworkResponse
並返回。NetworkResponse
並返回。Error
或 Response
並返回。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
供外界對請求過程進行監聽。