0%

Volley源码分析

前言

Volley 是 Google 2013 年推出的网络框架,主要是用来解决移动客户端和服务端的通信问题,其特点是扩展性好、使用简单,今天我们就来分析该框架的具体实现。

第一部分 Android中使用HTTP

在使用Volley之前,我们来看Android中如何使用HTTP与服务端通信,主要有如下两种方案

HttpClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void sendRequest() {
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://www.baidu.com");
HttpResponse httpResponse = httpClient.execute(httpGet);
System.out.println(httpResponse.toString());
} catch (Exception e) {
// ignore
}
}
}).start();
}

非常简单,主要代码也就三行。当然,如果想要使用HTTPS,只需要继承DefaultHttpClient覆写证书管理和校验部分即可,这里就不细说了。

UrlConnection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void sendRequest() {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection urlConnection = null;
try {
URL url = new URL("http://www.baidu.com/");
urlConnection = (HttpURLConnection)url.openConnection();
System.out.println(urlConnection.getInputStream().toString());
} catch (Exception e) {
// ignore
} finally {
urlConnection.disconnect();
}
}
}).start();
}

和HttpClient相比,代码也不长,主要代码也是三行。

小结

万丈高楼平地起,不管任何网络框架,都离不开基础。Volley进行网络请求时,最后还是会用到HttpClient和UrlConnection进行连接。

第二部分 Volley分析

网络框架

如果把Volley主要部分抽出来,组成一个最简单的网络框架,大概可以分为四部分

  • Request 描述一个请求
  • Reponse 负责解析请求结果
  • Network 提供接口,保证网络可靠连接
  • Manager(RequestQueue) 负责管理所有网络请求
    具体流程为:用户产生的Request,转发到Manager,Manager根据优先级将Request分发到Network,Network保证网络可靠传输,最后把结果投递给Reponse. 如下图所示

Volley 项目一览

图3基本上把Volley项目的中所有文件都列出来了,我们可以看到,Volley代码并不多,而且结构十分清晰

  • Request 构建请求
  • Response 响应结果
  • Cache 缓存
  • Network 网络层
  • ErrorHandler 错误回调
  • Executor 管理Request
  • Utils 工具类
    下面就让我们从代码角度分析Volley执行过程吧。

第三部分 Volley代码分析

我们来看一个请求是如何添加到队列中,执行并返回最终的Response的。

第一步 初始化

1
2
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(request);

新建一个队列管理所有的Request,队列新建的同时,会自动RequestQueue的start方法,在该方法中同时创建缓存线程和网络分发线程,并启动。

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
public class RequestQueue {
// 用来保存有相同Url的Request,因为对于可以缓存的Request来说,Url相同,意味着Response也应该相同
// 对于这类Request只需要请求一次即可,这是Volley优化点之一
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<String, Queue<Request<?>>>();
/**
* The set of all requests currently being processed by this RequestQueue. A Request
* will be in this set if it is waiting in any queue or currently being processed by
* any dispatcher.
* 保存正在处理或者正在等待的请求
*/
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
// 缓存队列,参考mWaitingRequests,该队列中的Request会交给CacheDispatcher进行处理
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
// 所有使用add方法添加的请求都会放在这里
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
// 负责分发响应结果,同时把结果投递到主线程中
private final ResponseDelivery mDelivery;

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();
}
}
}

第二部 添加请求

调用**RequestQueue.add(request)**方法把请求添加到队列中

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
public class RequestQueue {
/**
* 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);
}
// 添加标记
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 如果不需要缓存,直接把请求添加到网络队列中
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// mWaitingRequests保存多个Request是同一个Url的情况,
// 对于Request来说,同一个Url的Response也应该是相同的(request能走到这里说明request是可以缓存的),
// 因此这里把这些Request缓存起来,当有一个Request有返回值了,剩下的Request也没必要再去请求网络了
// 这是Volley优化点之一。
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
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 {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
}

其流程图如下

第三步 执行网络请求

在线程中开启一个死循环,不断从请求队列中拿Request

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
public class NetworkDispatcher extends Thread {
......
@Override
public void run() {
......
while (true) {
Request<?> request;
// 不断从队列中取请求
request = mQueue.take();
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
......
// 写入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 把结果投递给 Response
request.markDelivered();
mDelivery.postResponse(request, response);
......
}
}
}

此处的mNetwork其实是BasicNetwork,我们再看看BasicNetwork.performRequest方法都干了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class BasicNetwork implements Network {
......
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
// 省略部分代码
HttpResponse httpResponse = null;
// 请求头
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
// 执行网络请求,并得到响应报文
httpResponse = mHttpStack.performRequest(request, headers);
// 做一层包装后返回
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
//省略部分代码
}
}
}

BasicNetwork的performRequest方法中又调用了mHttpStack()的performRequest方法,mHttpStack其实是个接口,在Volley中有两种实现:HttpClientStack和HurlStack,我们以HttpClientStack为例,继续分析

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
/**
* An HttpStack that performs request over an {@link HttpClient}.
*/
public class HttpClientStack implements HttpStack {
protected final HttpClient mClient;
private final static String HEADER_CONTENT_TYPE = "Content-Type";
public HttpClientStack(HttpClient client) {
mClient = client;
}
//省略部分代码
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest);
}
//省略部分代码
}

非常简单,就是文中开头说的,使用HttpClient构建一个请求,然后返回响应信息。而HurlStack的performRequest方法,其实就是对应文中开头说的使用UrlConnection构建请求,这里就不再赘述了。流程图如下

这里的Network接口设计得十分巧妙,Network接口接收一个Request,返回NetworkResponse,任何人都可以通过实现Network完成自己的网络请求,可以使用HttpStack,也可以根据自己需要使用Socket通信,甚至可以使用底层TCP来通信,这使得框架非常灵活。

总结

不得不感叹Google工程师的架构设计能力,简单、精炼,扩展性非常好,源码可读性也非常高。

参考

volley源码解析(三)–Volley核心之RequestQueue类
Google Volley框架源码走读