Volley 源码阅读.

纯手打原创,转载请注明出处,感谢!

Volley项目是大概3年前谷歌出的一套网络访问框架,从出来之后我就接触过并在项目中有使用过,但是一直都是浅尝的使用,没有认认真真的去读过内部结构,
最近项目结束了一个版本,接着这个间隙我准备去重新读下这个三年前出来的代码.

Class Volley

Volley暴露出给我们最直接的类就是Volley这个类,其中有一个newRequestQueue的方法,来为我们创建一个RequestQueue.

1
RequestQueue mQueue = Volley.newRequestQueue(context);

我们就跟着这个方法开始读代码.

Class RequestQueue

这个类是比较关键的一个入口,该类的API告诉我们,这是一个Request 发送的队列, dispatchers 工作在线程池里.
我们可以使用 add(Request) 把一个我们希望的Request请求 加入队列中, 它会在在工作线程中从缓存中或者网络中解析这个请求,
并且从主线程传递出一个解析好的 Response.
其中RequestQueue的构造方法是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}

可以看出这个队列需要以下四个初始化参数

  • 用于把response缓存在磁盘的策略.
  • 一种履行Http请求的实现.
  • 用来发送网络请求的线程数量
  • 一种发送responses 和 errors的实现.
    其中Cache Network ResponseDelivery 都是Interface, 我们暂且只看他的工作流程不去看具体Impl,后面再回来看具体实现.
    当我们根据APi指示,调用RequestQueue的Start方法时,RequestQueue 才会真正开始工作.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /**
    * 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();
    }
    }

可以看出来我们初始化了一个CacheDispatcher, 它是extends 线程的类, 它是一个可以对请求队列中cache 进行分类的线程.
也初始化了一个NetWorkDispatcher数组, NetWorkDispatcher 也是extends 线程的, 他是一个负责把从队列中取出来Request 并且执行的线程.
接下来当我们有Request 希望发出的时候, 我们就需要调用 RequestQueue的add方法了.
我们一句句的跟着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
public <T> Request<T> add(Request<T> request) {
// 给这个request设置队列
request.setRequestQueue(this);
// 把这个request加入到一个HashSet中, 这个HashSet存着所有马上或者正在被任何dispacher处理中的request.
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// 给request 加上一些编号和marker
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");

// 如果当前这个request是不希望被缓存的, 那么他将直接被加入到NetworkQueue中去.
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}

synchronized (mWaitingRequests) {
// 如果这个Request是希望被缓存的,会先检查下WaitingRequests里有没有该Reuqest.
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<>();
}
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);
// 把该Request加入到mCacheQueue中.
mCacheQueue.add(request);
}
return request;
}
}

我们先来看不需要缓存的Request 被加入到mNetworkQueue之后的流程, 之前我们说过,RequestQueue 会有几个NetworkDispatcher 工作线程在run, 我们来看下其中的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
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 这里的mQueue 就是RequesQueue中的mNetworkQueue, request被加入到mNetworkQueue中以后,会被取出
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;
}
addTrafficStatsTag(request);

// 这里是执行request 请求的地方, 会返回一个NetworkResponse,这里的NetworkResponse是最基础的response raw数据.
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");

if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}

// 对networkResponse进行解析,其中这里的泛型是Request设置的,由request自己根据自己的需要 对response中的data 字节数组进行解析.
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");

// 这里需要主要的是 如果这个request需要做缓存, 那么将被存入Cache中.
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}

// 通过mDelivery返回 request 和 response. 注意这里是通过handler post到主线程的.
request.markDelivered();
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);
}
}
}

至此一次完整的 无缓存 Http 请求就结束了.
接下来可以观察下 需要缓存的Request 和 不需要缓存的Request 处理区别, 不难发现 需要缓存的Request请求 会加入到mCacheQueue中去, 而会处理CacheQueue的Dispatcher 正是
刚才我们初始化的mCacheDispatcher, 我们来看看mCacheDispatcher的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
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mCache.initialize();
while (true) {
try {
// 这里从我们刚才加入request的CacheQueue取出 Request, 如果request 是空 会卡住该线程,直到有Request入列.
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");

if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 这里会去Cache中找该Request的缓存, 如果找不到就会把他加入到mNetworkQueue 去执行首次请求.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
mNetworkQueue.put(request);
continue;
}

// 如果从缓存中查找到了这个Request的缓存, 但是它已经过期了,那么依旧会把他加入到mNetworkQueue 去再次执行请求.
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}

// 走到这说明我们拿到了这个Request的 有效缓存
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");

if (!entry.refreshNeeded()) {
// 如果这个Request 不需要更新, 那么到此该Http请求就结束了, 依旧是通过mDelivery 返回Request和response.
mDelivery.postResponse(request, response);
} else {
// 如果需要更新该request, 那么会在返回request和response的同时,再次去请求该Request.
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) {
// Not much we can do about this.
}
}
});
}

} catch (InterruptedException e) {
if (mQuit) {
return;
}
}
}
}

看到这里,Volley的大体框架 和工作流程就结束了,Volley这份代码虽然是三年前的框架,但是现在看依然是有很多可以学习的地方, 它的结构简单清晰, 可插拔性强, 层与层之间耦合低,都体现出了
它的水平.

Share Comments