本篇源码解析基于 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
供外界对请求过程进行监听。