本文大篇幅参考此篇文章,大家可以结合两篇文章看一下

1. Volley简介

在很早以前,如果Android开发者想使用网络请求的话,必须自己通过HttpClient或者HttpURLConnection编写代码来访问。但是他两的用法还是很复杂的,如果不适当的封装的话,就会有很多多余代码甚至效率降低。所以当时出现了很多第三方网络通信框架,但是都是第三方的,而谷歌官方一直没有作为。
最终在2013年,谷歌终于意识到了问题,于是他们推出了一个官方的全新的网络框架——Volley。Volley它又能非常简单的进行HTTP通信,又能轻松加载网络上的图片。他的设计目的就是应对数据量不大但是频发的网络操作,但是对于下载等需要大数据量的网络操作,他就不太适合。

2. 源码解析

2.1 从RequestQueue入手

如果你使用Volley的话,就会发现Volley不管进行什么操作,首先第一步就是先创建RequestQueue对象。
所以我们就可以认定他为Volley的入口。
创建RequestQueue的方法是RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());,我们就看看newRequestQueue干了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
// 在Android 2.3之前,HttpURLConnection是不可靠的。
// 请参阅:http://android-developers.blogspot.com/2011/09/androids-http-clients.html
// 在将来的某个时候,我们将把minsdkversion移到Android 2.2之上,
// 并可以删除这个回退(连同所有ApacheHTTP代码)。
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);
}

调用方法后,先查看Android版本是否大于等于2.3,如果大于则调用基于HttpURLConnectionHurlStack,否则调用基于HttpClientHttpClientStack。接下来创建BasicNetwork并调用newRequestQueue(context, network)方,我们再来看看这个newRequestQueue()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static RequestQueue newRequestQueue(Context context, Network network) {
final Context appContext = context.getApplicationContext();
// 对缓存目录使用惰性供应商,以便可以在主线程上调用newRequestQueue(),
// 而不会导致严格的模式冲突。
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;
}

可以看到,这个方法主要为Volley创建了一个硬盘缓存DiskBasedCache,然后通过这个磁盘缓存和Network创建了一个RequestQueue对象,并调用了start()方法,接下来我们看下start()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void start() {
stop();
// 确保当前运行的所有调度程序都已停止。
// 创建缓存调度器并开始它。
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();

// 创建达到池大小的网络调度程序(和相应的线程)。
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}

CacheDispatcher是一个缓存调度线程,并调用了start()方法。在循环中调用NetworkDispatcherstart()方法。NetworkDispatcher是网络调度线程,默认情况下mDispatchers.length为4,默认开启了4个调度线程,外加1个缓存调度线程,总共5个线程。
接下来Volley会创建各种Request,并调用RequestQueueadd()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public <T> Request<T> add(Request<T> request) {
// 将请求标记为属于此队列,并将其添加到当前请求集。
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
//mCurrentRequests是一个HashSet
mCurrentRequests.add(request);
}

// 按添加的顺序处理请求。
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
sendRequestEvent(request, RequestEvent.REQUEST_QUEUED);

// 如果请求是不可执行的,跳过缓存队列,然后直接进入网络。
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
mCacheQueue.add(request);
return request;
}

这块地方的代码就很简单了,就是根据requestshouldCache()方法来返回requestmShouldCache属性来判断是否可以缓存,默认是可以的。如果能缓存,将此请求加入mCacheQueue队列,不再重复请求;不可以的话就将请求加入网络请求队列mNetworkQueue

2.2 CacheDispatcher缓存调度线程

RequestQueueadd()方法并没有请求网络或者对缓存进行操作。当将请求添加到网络请求队 列或者缓存队列时,在后台的网络调度线程和缓存调度线程轮询各自的请求队列,若发现有请求任务则开 始执行。下面先看看缓存调度线程。

首先先来看看CacheDispatcheradd()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
public void run() {
if (DEBUG)
VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Process.THREAD_PRIORITY_BACKGROUND默认值为10

// 进行阻塞调用以初始化缓存。
mCache.initialize();

while (true) {
try {
processRequest();
} catch (InterruptedException e) {
// 可能被打断了,因为是时候要退出了。
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
VolleyLog.e(
"Ignoring spurious interrupt of CacheDispatcher thread; "
+ "use quit() to terminate it");
// 忽略cachedispatcher线程的假中断;
// 使用quit()终止它
}
}
}

这块可以看出主要就是初始化了缓存队列,然后开了个死循环,一直调用processRequest(),我们来看看这个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
private void processRequest() throws InterruptedException {
// 从CacheQueue中取出一个可用的request
final Request<?> request = mCacheQueue.take();
processRequest(request);
}

@VisibleForTesting
void processRequest(final Request<?> request) throws InterruptedException {
request.addMarker("cache-queue-take");
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_STARTED);

try {
//request如果被取消了,就直接返回
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
return;
}

Cache.Entry entry = mCache.get(request.getCacheKey());
// 没有缓存就把request添加到NetworkQueue中
if (entry == null) {
request.addMarker("cache-miss");
// 没有缓存,并且等待队列中也没有此request,那么就直接加入到NetworkQueue中
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}

// 如果缓存过期了,也是一样把request添加到NetworkQueue中
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}

// 有缓存并且没有过期
request.addMarker("cache-hit");
// 根据缓存的内容解析
Response<?> response =
request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
// 是否需要更新
if (!entry.refreshNeeded()) {
// 不需要更新,直接将结果调度到主线程
mDelivery.postResponse(request, response);
} else {
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
// 判断是否有相同缓存键的任务在执行
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
// 需要更新结果,先将结果调度到主线程,然后执行new runnable(){}
// runnable中就是将request添加到NetworkQueue中,更新一下内容
mDelivery.postResponse(
request,
response,
new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
});
} else {
// request已经加入到mWaitingRequests中
// 直接把结果调度到主线程
mDelivery.postResponse(request, response);
}
}
} finally {
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
}
}

我们在processRequest中可以看到有一个方法经常出现,那就是mWaitingRequestManager.maybeAddToWaitingRequests(request),它的作用是判断当前这个request是否有存在相同缓存键的请求已经处于运行状态,如果有,那么就将这个request加入到一个等待队列中,等到相同缓存键的请求完成。

总结一下CacheDispatcher主要步骤:

  • CacheQueue中循环取出request
  • 如果缓存丢失,加入到NetworkQueue中;
  • 如果缓存过期,加入到NetworkQueue中;
  • 将缓存中的数据解析成Response对象;
  • 如果不需要更新,直接将结果回调到主线程,回调操作等介绍完NetworkDispatcher之后一起深入剖析;
  • 如果需要更新,先将结果回调到主线程,然后再将request加入到NetworkQueue中。

下面来看看网络调度线程。

2.3 NetWorkDispatcher网络调度线程

NetworkDispatcher的run方法代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
try {
processRequest();
} catch (InterruptedException e) {
// 我们可能被打断了,因为是时候退出了。
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
VolleyLog.e(
"Ignoring spurious interrupt of NetworkDispatcher thread; "
+ "use quit() to terminate it");
}
}
}

由此可以看出,NetWordDispatchCacheDispatch非常类似。
他的run()方法和CacheDispatch的方法基本一样,这就不多做介绍,下面来看看他的processRequest()方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
private void processRequest() throws InterruptedException {
// 从NetworkQueue中取出request
Request<?> request = mQueue.take();
processRequest(request);
}

@VisibleForTesting
void processRequest(Request<?> request) {
long startTimeMs = SystemClock.elapsedRealtime();
request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
try {
request.addMarker("network-queue-take");

// 如果request被取消了,那么就不执行此request
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
return;
}

addTrafficStatsTag(request);

// 还记得这个mNetwork么,它就是Volley.newRequestQueue()方法里的BasicNetwork对象,一会我们来看看mNetwork.performRequest()方法是如何得到NetworkResponse的
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");

// notModified是服务端返回304,hasHadResponseDelivered()是request已经回调过了
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
}

// 将NetworkResponse解析成Response对象,在子线程中执行
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");

// 将request写入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}

request.markDelivered();
// 回调结果至主线程
mDelivery.postResponse(request, response);
request.notifyListenerResponseReceived(response);
}
// 以下都是处理异常错误,然后也需要回调至主线程
catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
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);
mDelivery.postError(request, volleyError);
request.notifyListenerResponseNotUsable();
} finally {
request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_FINISHED);
}
}

通过NetworkDispatcher.processRequest()方法可以发现,主要分为以下几步:

  • 通过BasicNetwork.performRequest(request)得到NetworkResponse对象;
  • 通过request.parseNetworkResponse(networkResponse)解析得到Response对象;
  • 通过mDelivery将成功结果或者失败结果回调到主线程。

现在我们依次来分析这三步:

  • 请求网络,得到NetworkResponse
    我们看看BasicNetworkperformRequest()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@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 {
// Gather headers.
Map<String, String> additionalRequestHeaders =
getCacheHeaders(request.getCacheEntry());
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
int statusCode = httpResponse.getStatusCode();

responseHeaders = httpResponse.getHeaders();
// Handle cache validation.
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);
}
…………省略

通过上面源码可以看出,BasicNetwork就是封装了一下NetworkResponse,并没有涉及到网络请求,我们继续深入到BaseHttpStack.executeRequest(request, additionalRequestHeaders)源码中。

1
2
3
public abstract HttpResponse executeRequest(
Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;

这时发现这个BaseHttpStack就是一个抽象类,而这个executeRequest()也就是一个抽象方法。我当时就卡在这了,调用了一个抽象类的抽象方法,这咋操作嘛。然后我就好好再看了一遍,找到BasicNetwork的构造函数中对mBaseHttpStck定义的地方,发现这个是构造函数传进来的,然后就想到了在调用Volley.newRequestQueue()时,是根据Android版本传入了不同的Stack,那我们就来看看HurlStack.executeRequest()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@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()优先于给定的附加(缓存)头.
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);
// 使用来自httpurlConnection的数据初始化httpResponse。
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// 如果无法检索响应代码,getResponseCode()将返回-1。
// 向呼叫者发出信号,说明连接有问题。
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}

if (!hasResponseBody(request.getMethod(), responseCode)) {
return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()));
}

// 需要保持连接打开,直到调用方使用流。包装流,以便close()将断开连接。
keepConnectionOpen = true;
return new HttpResponse(
responseCode,
convertHeaders(connection.getHeaderFields()),
connection.getContentLength(),
new UrlConnectionInputStream(connection));
} finally {
if (!keepConnectionOpen) {
connection.disconnect();
}
}
}

可以看到,主要就是借助了HttpURLConnection对象来请求网络,并根据不同的条件返回不同的HttpResponse对象。

  • 解析NetworkResponse, 得到Response
    解析过程是定义在Request类中,但是他是一个抽象类,不同的Request都有自己的实现,我们现在就以JsonRequest为例看看:
    然后发现他又是一个抽象类,那我们就看看JsonRequest其中一个实现类JsonObjectRequestparseNetworkResponse()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(
response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(
new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}

这个就不用多说了,根据返回来的response建了一个String然后把这个String放到Response里面去然后再返回去。

  • 回调主线程
    回调主要是通过DeliverypostResponse()方法实现的,我们来看看这个方法,找过去又找到了一个ResponseDelivery抽象类,然后又得找他的实现类,这时大家应该记得RequestQueue()的时候初始化了一个Delivery
1
2
3
4
5
6
7
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(
cache,
network,
threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

他返回了一个ExecutorDelivery,我们来看看这个类,然后就惊喜的发现,我们终于找到我们需要的东西了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 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);
}
};
}

知道了它的初始化,我们再来看看它是如何实现回调的:

Volley中回调是通过postResponse()方法的 :

1
2
3
4
5
6
7
8
9
10
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));
}

postResponse()最终会调用mResponsePoster对象的execute()方法,传入了一个ResponseDeliveryRunnable对象,它实现了Runnable接口,execute()方法会通过Handler.post(runnable)ResponseDeliveryRunnable放入消息队列。最后我们来看看这个ResponseDeliveryRunnablerun()方法在主线程中做了什么操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@SuppressWarnings("unchecked")
@Override
public void run() {

// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}

if (mResponse.isSuccess()) {
// 执行成功的回调,在具体的Request实现类中,比如StringRequest就会调用listener.onResponse(string)回调
mRequest.deliverResponse(mResponse.result);
} else {
// 执行失败的回调,在request中,直接回调了listener.onErrorResponse(error)
mRequest.deliverError(mResponse.error);
}

// intermediate默认为false,但是在CacheDispatcher的run()中,如果需要更新缓存,那么就会置为true
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}

// 如果传入了runnable不为空,那就就执行runnable.run()方法
// 回忆下在CacheDispatcher的run()方法中,如果request有缓存,但是需要更新缓存的时候,mDelivery是不是调用的带runnable的方法
if (mRunnable != null) {
mRunnable.run();
}
}

3 请求流程图

最后附上Volley的请求流程图
VolleyRequestProcess