Volley源码解析

抽时间看了下Volley的源码,感觉这个框架还是很不错的,这里对其源码进行分析。

主要从以下几个方面分析:

  1. 简要介绍 Volley 的使用
  2. Volley 访问网络的整体流程
  3. 对源码中一些重要的类的总结
  4. 一些总结

简要介绍 Volley 的使用

这个不是文章的重点,所以就简单介绍一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 创建请求队列
RequestQueue mQueue = Volley.newRequestQueue(context);
// 创建请求
StringRequest stringRequest = new StringRequest("http://www.baidu.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
// 添加请求到队列中
mQueue.add(stringRequest);

更具体的使用可以参考博客 Volley使用

Volley 访问网络的整体流程

这里从使用方法开始进行分析。

首先通过 Volley#newRequestQueue() 创建请求队列。
在Volley类中,一共就提供了两个静态的重载方法 newRequestQueue()
主要看下 newRequestQueue(Context, HttpStack)

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
// Volley#newRequestQueue()
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
// 创建 HttpStack
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) { // 根据sdk版本选择HttpUrlConnection或者HttpClient
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack); // 封装网络请求的一些操作
// 创建请求队列并启动
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}

可以看到,其中主要创建了Network,HttpStack,RequestQueue,并启动了RequestQueue
最终网络访问就封装在HttpStack中,这个后面再看
先跟进到 RequestQueue#start()中看下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// RequestQueue#start()
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// 启动缓存派发器
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();
}
}

其中可以看到,就是启动了缓存派发器和网络派发器

在构建了请求队列后,发起请求只要把请求添加到请求队列中就好了,接下来看下RequestQueue#add()代码
这里挑一些主要的代码看

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
// RequestQueue#add()
// RequestQueue中维护了两个队列,一个是缓存队列,一个是网络请求队列,之后派发器会从这两个队列中获取请求
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
public <T> Request<T> add(Request<T> request) {
// 省略 ...
// 如果请求不需要缓存的话,直接交给网络请求队列去执行,默认是需要缓存的
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// 已经有相同请求正在执行的时候,就吧这个请求保存起来
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// 没有相同的请求在执行中,就把这个请求放入缓存队列中
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}

从上面代码中可以看到,请求需要缓存的话,会加入到缓存队列中,交给缓存派发器处理,否则交给网络请求派发器处理
接下来看下缓存派发器对请求的处理
由于 CacheDispatcher 和 NetworkDispatcher 都是继承自 Thread , 所以主要看其 run() 函数

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
// CacheDispatcher#run()
public void run() {
// 对缓存池进行初始化
mCache.initialize();
while (true) { // 死循环不断处理请求
try {
// 从缓存队列中获取请求,这个缓存队列就是 RequestQueue 中看到的缓存队列
// 如果队列为空,会阻塞在这个地方
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 请求取消什么都不做
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 从缓存池中获取请求缓存的内容
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// 缓存池中没有缓存此请求,也加入到网络请求队列中去
mNetworkQueue.put(request);
continue;
}
// 请求过期,也加入网络请求队列
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
request.addMarker("cache-hit");
// 根据缓存中获取的内容构建请求的回复
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// 缓存不需要刷新,直接交给 ResponseDelivery 去处理,ResponseDelivery后面再看
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
// 如果缓存需要刷新,这个地方还需要再重新请求网络
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
}
}

从上面可以看到,代码不多,也很好理解,如果有缓存,就取出来去处理,没有或者过期,就加入到网络请求队列中去请求,需要网络请求的有几个地方:1. 请求不需要缓存 2. 请求在缓存中没有找到 3. 请求的缓存过期 4. 请求的缓存需要刷新
接下来就看下网络请求派发器
NetworkDispatcher 也是继承 Thread,所以也直接看 run() 函数

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
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) { // 一样是死循环不断处理请求
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 这个地方会阻塞,这个队列就是 RequestQueue 中的网络队列
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 请求取消就什么都不做
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// 执行网络请求 其中真正执行的是 httpStack.performRequest
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 对返回的内容进行处理,parseNetworkResponse 需要我们重写,不过框架提供了一些常用的比如 StringRequest 和 JsonRequest
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");
}
request.markDelivered();
// 交给 ResponseDelivery 进一步处理
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} 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);
}
}
}

可以看到,在 NetworkDispatcher 中,最主要的就是调用了 mNetwork.performRequest(request) 执行网络请求,而在 Network 是个接口,具体实现类是 BasicNetwork,在BasicNetwork#performRequest()中调用 httpResponse = mHttpStack.performRequest(request, headers),这里的 mHttpStack 就是前面看到的 HurlStack 和 HttpClientStack。而在 HttpStack 的 performRequest() 中就是具体的网络请求,看下 HurlStack 的。

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
// 这块就比较熟悉了,就是之前经常用的网络请求
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
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);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
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.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}

在上面我们看到,最终会调用 ResponseDelivery#postResponse()
ExecutorDelivery 是 ResponseDelivery 的实现类,可以看下 ExecutorDelivery#postResponse()

1
2
3
4
5
6
7
8
9
// ExecutorDelivery#postResponse()
private final Executor mResponsePoster;
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
// 执行任务
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
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
// ResponseDeliveryRunnable#run()
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 和 JsonRequest 中都是直接调用了 listener.onResponse()
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// 如果有需要执行的任务,会执行,前面缓存需要刷新的话,就会在这个地方进行网络调用
if (mRunnable != null) {
mRunnable.run();
}
}

以上就是请求的主要流程了,下面附上一张流程图
volley流程图

上面分析中没有提到 Volley 中的重试机制,其实在 BasicNetwork#performRequest() 中实现了失败重试机制。

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
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
// 这个循环保证了失败后可以重新请求
while (true) {
// 省略代码 ...
try {
// 执行网络请求
httpResponse = mHttpStack.performRequest(request, headers);
// 省略代码 ...
} catch (SocketTimeoutException e) {
// 这里通过捕获异常来重试,这里没有捕捉 VolleyError 的异常,所以抛出 VolleyError 异常就会结束循环,不再重试了
attemptRetryOnException("socket", request, new TimeoutError());
}
// 省略代码 ...
}
}
private static void attemptRetryOnException(String logPrefix, Request<?> request,
VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
retryPolicy.retry(exception); // 重试策略,如果不需要重试,就会抛出 VolleyError 的异常
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}

对源码中一些重要的类的总结

其实在上面讲流程的时候已经分析了主要的类了,这里主要做个总结。
Volley中主要的类有这些:

  • Volley: 提供了构建 RequestQueue 的统一方法,我们也可以不通过这个而是自己构建 RequestQueue
  • RequestQueue: 负责分发请求到不同的请求队列中
  • CacheDispatcher: 处理缓存请求
  • NetworkDispatcher: 处理网络请求
  • ResponseDelivery: 获取请求后进行处理
  • Cache: 缓存接口,具体实现类有 DiskBaseCache
  • Network: 网络接口,具体实现类有 BasicNetwork
  • HttpStack: 真正执行请求,具体实现类有 HurlStack HttpClientStack
  • Request: 封装请求信息并处理回复,具体实现类有 StringRequest JsonRequest
  • Response: 封装返回的信息,具体实现类有 NetworkResponse

一些总结

最后再分析一下为什么在 Volley 的 listener 的中可以直接更新UI。
在前面 Volley#newRequestQueue() 中有调用到如下代码

1
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);

跟进会发现最后调用的是

1
2
3
4
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

我们上面也提到,最后的 Response 是通过 ResponseDelivery#postResponse() 来分发,而 ExecutorDelivery 是 ResponseDelivery 的实现类,而这个构造函数的实现如下:

1
2
3
4
5
6
7
8
9
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); // 请求最终都会调用 handler.post(),而这个 handler 是上面通过 new Handler(Lopper.getMainLopper()) 得到的
}
};
}

请求最终都会调用 handler.post(),而这个 handler 是上面通过 new Handler(Lopper.getMainLopper()) 得到的,所以请求的处理就通过这里分发到主线程中了,也就自然可以更新 UI 了。

最后:
通过对 Volley 源码的分析,可以发现, Volley 框架的拆装性很强,框架默认使用的是 HttpUrlConnection 和 HttpClient 来实现网络请求,如果我们想换成其他,比如okhttp,只要继承 HttpStack 并重写 performRequest() 来处理请求就可以了。对于请求,框架默认提供了 StringRequest 和 JsonRequest,一般情况下这两个是足够用了,但是特别情况下,我们只要继承 Request 并重写 deliverResponse() 和 parseNetworkResponse() 就可以了。
以前自己也有实现过网络框架,相比之下,还是有很多不足,通过阅读源码,还是学到了很多。

热评文章