这篇博客想写挺久的了,因为之前接触到网络请求的第一个流行的库就是Volley,在使用的时候也感觉到它的功能和拓展性的强大,但是一直没有去探究他内部的流程,同时可以发现网上对Volley这个框架设计的评价都非常好,所以去了解内部的实现还是很有必要的。下面开始吧。

  先从最开始怎么去使用说起,最常见的使用方式就是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest request = new StringRequest("http://70kg.info", new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
queue.add(request);

先去创建一个RequestQueue,然后在去创建一个request,最后加入到队列里面就可以了。使用也是相当的方便清晰。那就从RequestQueue入手,看看这个队列是什么样的。

RequestQueue

这个类是在toolbox包里面,都是一下已经给我们实现一些功能的类,方便我们去使用。Volley.newRequestQueue(this);最终走到了这里:

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
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) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
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;
}

这里可以看到还有个userAgent,在使用使用HttpClientStack也就是API<9,使用`androidhttpclient`的时候使用,具体是什么会在下面的参考中给出,和本文关系不是很大,只要明白在>=9的时候使用了HurlStack,内部是HttpURLConnection就差不多了。下面还有个Network,是根据传进去的stack来选择不同的处理网络方式,后面会说。最重要的就是下面两行,真正的new RequestQueue,然后star,进去看看:

经过各种重载构造函数,走到了这个:

1
2
3
4
5
6
7
8
9
10
11
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
//默认缓存
mCache = cache;
//处理网络的
mNetwork = network;
//一个NetworkDispatcher数组,默认大小4
mDispatchers = new NetworkDispatcher[threadPoolSize];
//一个ExecutorDelivery对象
mDelivery = delivery;
}

然后就是去看start方法了:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void start() {
stop();
//一个缓存调度线程
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
//默认4个网络调度线程
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}

所以当我们新建了一个RequestQueue之后,其实是启动了5个线程在跑。接下来就是queue.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
public Request add(Request request) {
//相当于设个tag为当前的queue
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
//private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();正在进行,尚未完成请求的集合
mCurrentRequests.add(request);
}
//序列号 可以理解为加入的顺序
request.setSequence(getSequenceNumber());
//添加个标记
request.addMarker("add-to-queue");
//不缓存,就直接加入网络队列去请求网络
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
//维护了一个等待请求的集合,如果一个请求正在被处理并且可以被缓存,后续的相同 url 的请求,将进入此等待队列。
if (mWaitingRequests.containsKey(cacheKey)) {
//前面有相同的了 等待
Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
} else {
//否则加入缓存队列
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}

CacheDispatcher

Volley默认都是可以缓存的,而处理缓存的是CacheDispatcher,这是一个线程,那就可以去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
@Override
public void run() {
//线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//初始化缓存
mCache.initialize();
//开启无限循环
while (true) {
try {
//从缓存队列中获取第一个
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
request.addMarker("cache-hit");
//转换成一个NetworkResponse
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
//mDelivery使用handler分发到主线程
mDelivery.postResponse(request, response);
} else {
//检验新鲜度 request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
//分发 再去加入网络队列检验新鲜度
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
}
}
});
}
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
}
}

整个过程差不多都注释了,再来个大招,非常棒的流程图:

CacheDispatcher

说完缓存分发,还有网络分发,还是看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
@Override
public void run() {
//线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request request;
//循环取
while (true) {
try {
//取一个
request = mQueue.take();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
if (request.isCanceled()) { request.finish("network-discard-cancelled");
continue;
}
// Tag the request (if API >= 14)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
//返回304并且已经处理过这个,跳过
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
//解析成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");
}
request.markDelivered();
//分发到主线程
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
}

整个流程和缓存差不多,再来一个图:

Network

在上面的网络分发中看到了主要就是mNetwork.performRequest(request);这句话执行了真正的网络请求操作,而Network是一个接口,实现类是在创建RequestQueue时候创建的BasicNetwork,看看这个里面的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BasicNetwork implements Network {
//...省略看不懂的网络操作
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
//...
//这就是前面根据版本不同选择不同的网络处理方式
httpResponse = mHttpStack.performRequest(request, headers);
//....
//组装返回值
networkResponse = new NetworkResponse(statusCode, responseContents,responseHeaders, false);
}
//省略
}

到现在请求的结果也回来了,那么具体是怎么去分发Response的呢?主要是这句话mDelivery.postResponse(request, response);
ResponseDelivery是一个接口,实现类是创建RequestQueue的时候创建的public class ExecutorDelivery implements ResponseDelivery,看一下它的postResponse方法,主要就是mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));可以理解为向主线程发送了一个Runnable而在这个Runnable里面呢:

1
2
3
4
5
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}

第一个方法是不是就比较熟悉了,就是自定义Request的时候要重写的两个方法之一,一般就直接mListener.onResponse(response);就可以了,而错误的分发Request基类已经实现完了。
到这,基本的过程差不多了,还有重试策略和缓存的具体没写,大概就这样吧。

参考:

详细解读Volley(五)—— 通过源码来分析业务流程

Volley 源码解析

Volley源码分析