文章目錄
  1. 1. 流程图
  2. 2. 创建 RequestQueue
    1. 2.1. HttpUrlConnection 和 HttpClient 的区别
    2. 2.2. RequestQueue 构造方法
    3. 2.3. start()
  3. 3. 请求入队
  4. 4. CacheDispatcher 缓存调度
  5. 5. NetworkDispatcher 网络调度
  6. 6. 分发响应数据
  7. 7. 请求完成及取消
  8. 8. 优先级
  9. 9. NetWork 网络传输
  10. 10. 结语

转载请附原文链接: Volley 源码学习笔记

这几天开启了疯狂的源码学习模式,今天学习的是网络框架 — Volley。依旧按照一贯的思路,从用法入手,再全面分析。

其实把 Volley 理解成为一个 Request (请求)的调度器可能更容易学习。

本次分析主要涉及到的类:

  • Volley — 获取队列的帮助类
  • RequestQueue — 请求队列,维护请求调度
  • NetWorkDispatcher — 网络请求线程
  • CacheDispatcher — 缓存线程
  • Request — 请求

流程图

volley流程图

上面是一张 Volley 处理请求的流程图,简单介绍一下有一个大体的认识,再看源码的时候理解起来会相对容易很多。从上到下依次介绍

  • 首先会把这个请求加入到一个当前请求的队列中,在这个队列中的请求都表示被 Volley 处理过。在后面结束或者取消请求的时候需要用到
  • 接着判断是否需要缓存,需要走左边,不需要走右边直接加入 网络请求队列中
  • 左边是一个等待队列,用于判断当前是否有相同的请求正在进行,如果有则相同的请求都放到这个等待队列中不马上执行。如果没有则向下放到缓存队列中,同时在等待队列中标记该请求
  • 如果请求被加入到等待队列中了,当正在进行的那个请求被执行完毕后,会将等待队列中重复的请求都放到缓存队列中,也就是直接使用缓存数据而不请求网络了。

这是大致的流程,实际上的处理还有很多小细节,后面几节慢慢的来学习。

创建 RequestQueue

用过 Volley 的同学都知道,使用前应该先获取一个请求队列,然后创建请求,再把请求加入到队列当中去。一般在 Android 中会获取一个全局单例的队列,便于对请求的统一管理。首先来看获取队列的代码。

1
RequestQueue mRequestQueue = Volley.newRequestQueue(this);

来看看 Volley 是怎么创建请求队列的,上面的这个方法调用下面这个方法,传入一个空的 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
31
32
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
// 1.在 api9 以上的情况使用 HurlStack -- 实际上就是 HttpUrlConnection
// 2.api9 一下则使用 HttpClient 作为网络请求
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
// 通过 stack 创建 Network 处理网络请求
Network network = new BasicNetwork(stack);
// 创建一个请求队列
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
// 开始运行队列
queue.start();
return queue;
}

上面的代码就是创建了一个 RequestQueue 对象,指定了缓存策略和网络请求处理。这里要插一句,为什么区分了一下 SDK 的版本呢?

HttpUrlConnection 和 HttpClient 的区别

HttpClient 是一个网络框架,封装得较为完善,提供了大量的 API 给调用者,性能上也比较稳定。但是这也导致 HttpClient 在保持兼容的情况下扩展性并不是很好,官方也没有很积极的去维护。甚至在 6.0 上将这个框架给废弃。

相反 HttpUrlConnection 就是一个十分轻量的框架,使用上虽然麻烦一点,扩展性很强。但是在 API9 以前这个框架的 bug 很多,表现不稳定。因此在 API9 以前 Volley 是使用的 HttpClient。

在 API9 以后,HttpUrlConnection 修复了许多 bug,并且支持响应压缩 gzip,甚至在 4.0 上提供了缓存机制。因此在 API9 以上 Volley 是使用的 HttpUrlConnection

RequestQueue 构造方法

RequestQueue 创建的时候初始化了几个变量。可以看到默认的网络线程数是四个,而 mDisPatchers 则是一个长度为线程数的数组,而默认的 ExecutorDelivery 是传入了一个主线程的 Handler 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;// 缓存
mNetwork = network; // 网络
mDispatchers = new NetworkDispatcher[threadPoolSize]; // 调度器
mDelivery = delivery; // 分发 response
}

构造方法中看不出什么特别的端倪,接着看 start() 方法

start()

在 start() 方法里面首先确保将所有的调度器都被停止了。

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

这段代码没多少东西,开启了一个缓存调度器,和四个(默认)网络调度器。还是没有什么头绪,先别急着去看调度器里面的代码,咱们先看看请求是如何加入到队列中去的。

请求入队

这段代码比较长,代码用注释来分析。事实上作者的注释也写得十分详尽

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
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 的队列设置为该队列(在 request中后面需要调用队列中的方法)
request.setRequestQueue(this);
// 该集合中存放当前被处理过的请求
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
// 给请求设置序列号
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
// 如果这个请求不需要缓存,则直接加入到网络队列中
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
// 走到这里说明请求需要被缓存
synchronized (mWaitingRequests) {
// 获取到缓存的 key
String cacheKey = request.getCacheKey();
// 如果暂存集合中只要能找到这个 key,说明当前有一个相同的请求正在处理中
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
// 获取到暂存集合中这个 key 对应的请求队列(重复请求可能会有多个)
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 {
// 到这里就说明当前没有相同的请求在等待处理
// 将 cacheKey 放入到暂存集合中来标记当前已经有一个请求在等待处理了
// 只做标记,值为空
mWaitingRequests.put(cacheKey, null);
// 将请求放到缓存队列中去
mCacheQueue.add(request);
}
return request;
}
}

注意这几件事情

  • 只要进入到了 add () 方法中的 Request 就表示被处理过的,都要放到 当前请求的集合中,这个集合中就是目前整个队列中存在的请求
  • 如果不需要缓存,则请求都应该请求网络,因此放到网络请求的队列中
  • 暂存集合中只要能找到相同的 key 则说明有相同的请求正在等待执行,即便通过 key 获取的值是空
  • 重复的请求需要待在暂存集合中,并不会马上被加入到队列中

来看看网络队列和缓存队列是什么鬼,发现两个都是优先阻塞队列,这个后面会讲到。

1
2
3
4
5
6
7
/** The cache triage queue. */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
/** The queue of requests that are actually going out to the network. */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();

到这里好像请求就添加完成了,那么请求是怎么被处理的呢?

CacheDispatcher 缓存调度

如果有缓存的情况下,请求被加入到了缓存队列中,那就先来看看缓存调度器是怎么工作的。

CacheDispatcher 继承自 Thread 类,复写了 run() 方法。先看看构造方法,在请求队列 start() 方法中把缓存队列,网络队列,缓存策略,以及发送器都传了进来。

1
2
3
4
5
6
public CacheDispatcher(BlockingQueue<Request<?>> cacheQueue,BlockingQueue<Request<?>> networkQueue,Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}

接下来就是 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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
// 初始化缓存目录
mCache.initialize();
// 进入一个死循环
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
// 从缓存队列中获取一个请求,该方法是阻塞式的,直到获取到请求
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
// 如果这个请求被取消了,别犹豫,干掉它~
// 进入下一次循环
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
// 通过 mCache 去获取该请求在磁盘上的缓存
Cache.Entry entry = mCache.get(request.getCacheKey());
// 如果获取缓存失败 -- miss,则将这个 request 加入到网络队列中
// 进入下一次循环
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
// 到这里说明获取到了缓存,检查缓存是否过期,失效了
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
// 缓存过期了,还是要请求网络
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");
// 到这儿说明拿到了缓存,并且没有过期,解析 response
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
// 需要判断一下,如果缓存不需要刷新,发送响应即可
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
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.
// 这里的逻辑是缓存没有过期,但是缓存需要更新了
// 1.拿到的缓存依然需要直接分发出去
// 2.同时将这个请求扔到网络队列中去获取新的数据
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
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;
}
}
}

总结一下缓存调度器的四种状态

  • miss — 失败,请求网络
  • expire — 过期,请求网络
  • hit — 成功,分发数据
  • needRefresh — 成功并需要更新,先分发数据,再请求网络

NetworkDispatcher 网络调度

NetWorkDispatcher 同样继承自 Thread 类,重写了 run() 方法。因为在这里不需要关心缓存,所以构造方法中不需要传入缓存队列

1
2
3
4
5
6
public NetworkDispatcher(BlockingQueue<Request<?>> queue,Network network, Cache cache,ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}

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
78
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
// 请求开始时间
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
// 从队列中获取一个请求,也是阻塞方法
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 the request was cancelled already, do not perform the
// network request.
// 如果这个请求被取消了,同样,不要请求网络,干掉它
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
// 这里是请求网络,获取到 networkResponse
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.
// 如果服务器返回了304 并且数据已经发送过一次了,不要再发送第二次,干掉
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
// 解析response
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
// 需要缓存,将请求返回的数据存到磁盘上
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back.
// 标记
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);
}
}
}

分发响应数据

请求处理完了,返回的数据应该怎么返回呢?在上面的代码中通过这个方法 mDelivery.postResponse(request, response) 来发送数据

1
2
3
4
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
1
2
3
4
5
6
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}

还记得之前构造请求队列的时候初始化了这货,handler 是跟主线程 looper 绑定的

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

这里的 command 就是上面代码中的 ResponseDeliveryRunnable ,点进去瞧一眼实现了 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
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}

mRequest.deliverResponse(mResponse.result),这是 Request 中的一个抽象方法,在子类中被实现,挑一个 JsonReqeust 实现的随意感受下,是不是就明白了。

1
2
3
4
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}

请求完成及取消

数据是分发完了,别忘了请求还在各种乱七八糟的集合里面,得处理一下。先来看 request.finish() 方法,下面的代码只贴了核心代码,另外一部分跟 log 相关的没有贴出

1
2
3
4
5
void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
}

调用的是请求队列里的 finish()

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
<T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed.
// 首先从现有的请求集合中移除掉该请求
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
// 回调监听
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
// 如果这个请求有缓存
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
// 从暂存集合中,把重复的请求都取出来
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
// 全部加入到缓存队列中去
mCacheQueue.addAll(waitingRequests);
}
}
}
}

finish() 干了三件事情

  • 把这个请求从现有集合中移除掉,因为已经结束了
  • 回调所有的监听
  • 把暂存集合中的重复请求加入到缓存队列中

如果是取消呢?cancelAll 里面是怎么处理的,下面这个是我们常用的方法,传入一个tag,取消所有该tag的请求

1
2
3
4
5
6
7
8
9
10
11
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}

创建一个 RequestFilter 过滤器,条件是 request 的 tag 与传入 tag 相同,然后依次调用 cancel(),还记得前面多处地方判断当前请求是否被 cancel ,是就直接调用 finish。

1
2
3
4
5
6
7
8
9
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}

优先级

我们知道如果要给一个请求设置优先级,需要重写一个方法 getPriority,默认返回的是 NORMAL ,

1
2
3
4
5
6
7
8
9
10
public Priority getPriority() {
return Priority.NORMAL;
}
public enum Priority {
LOW,
NORMAL,
HIGH,
IMMEDIATE
}

那入队的时候是怎么判断优先级的呢?Volley 好像也没有处理啊。

Volley 确实没有处理,还记得一开头介绍两个队列的时候提了一嘴,优先阻塞队列 — PriorityBlockingQueue,这个类实现了 BlockingQueue 接口,首先是一个阻塞队列

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 boolean add(E e) {
return offer(e);
}
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
int n, cap;
Object[] array;
while ((n = size) >= (cap = (array = queue).length))
tryGrow(array, cap);
try {
Comparator<? super E> cmp = comparator;
// 判断是否有对比器
if (cmp == null)
// 没有设置对比器,直接对比 comparable
siftUpComparable(n, e, array);
else
// 利用对比器对比
siftUpUsingComparator(n, e, array, cmp);
size = n + 1;
notEmpty.signal();
} finally {
lock.unlock();
}
return true;
}

如果没有传入对比器,那么被存储的对象应该实现了 Comparable 接口,在这个方法里面会根据 compareTo 方法来比对两个元素的大小,决定放在队列的哪个位置

1
2
3
4
5
6
7
8
9
10
11
12
private static <T> void siftUpComparable(int k, T x, Object[] array) {
Comparable<? super T> key = (Comparable<? super T>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = array[parent];
if (key.compareTo((T) e) >= 0)
break;
array[k] = e;
k = parent;
}
array[k] = key;
}

Request 正是实现了 Comparable 接口,并且按照优先级重写了 compareTo 方法

1
2
3
4
5
6
7
8
9
10
11
@Override
public int compareTo(Request<T> other) {
Priority left = this.getPriority();
Priority right = other.getPriority();
// High-priority requests are "lesser" so they are sorted to the front.
// Equal priorities are sorted by sequence number to provide FIFO ordering.
return left == right ?
this.mSequence - other.mSequence :
right.ordinal() - left.ordinal();
}

NetWork 网络传输

Volley 默认是使用 BasicNetWork(HttpStack) 来做网络传输,重写了 performRequest 这个方法,在这个方法里面调用 HttpStack 的 performRequest 方法。

1
httpResponse = mHttpStack.performRequest(request, headers);

贴出了 HurlStack 的代码,可以看到实际上就是使用的 HttpUrlConnection, 而且我猜 Hurl 是简称吧~

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
@Override
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;
}

这一节没什么可讲的,其实就是说明 Volley 其实理解为一个调度器可能更贴近于它的功能,如果你需要完全可以把网络传输进行自定义,把线程调度交给 Volley,这也是它灵活强大的地方。

结语

通过这么久的源码学习,我发现看源码是消除误区和误解的最佳办法。并且能学习到大牛的编码风格,设计思路等等。

下一篇学习笔记中,我将分析 Volley 中的 ImageLoader 以及 LruCache 这个两个类,看看在 Android 处理图片的相关技术。