博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
volley4--RequestQueue
阅读量:7202 次
发布时间:2019-06-29

本文共 26620 字,大约阅读时间需要 88 分钟。

源码:

1 /*  2  * Copyright (C) 2011 The Android Open Source Project  3  *  4  * Licensed under the Apache License, Version 2.0 (the "License");  5  * you may not use this file except in compliance with the License.  6  * You may obtain a copy of the License at  7  *  8  *      http://www.apache.org/licenses/LICENSE-2.0  9  * 10  * Unless required by applicable law or agreed to in writing, software 11  * distributed under the License is distributed on an "AS IS" BASIS, 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13  * See the License for the specific language governing permissions and 14  * limitations under the License. 15  */ 16  17 package com.android.volley; 18  19 import android.os.Handler; 20 import android.os.Looper; 21  22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.HashSet; 25 import java.util.LinkedList; 26 import java.util.List; 27 import java.util.Map; 28 import java.util.Queue; 29 import java.util.Set; 30 import java.util.concurrent.PriorityBlockingQueue; 31 import java.util.concurrent.atomic.AtomicInteger; 32  33 /** 34  * A request dispatch queue with a thread pool of dispatchers. 35  * 36  * Calling {@link #add(Request)} will enqueue the given Request for dispatch, 37  * resolving from either cache or network on a worker thread, and then delivering 38  * a parsed response on the main thread. 39  */ 40 public class RequestQueue { 41  42     /** Callback interface for completed requests. */ 43     public static interface RequestFinishedListener
{ 44 /** Called when a request has finished processing. */ 45 public void onRequestFinished(Request
request); 46 } 47 48 /** Used for generating monotonically-increasing sequence numbers for requests. */ 49 private AtomicInteger mSequenceGenerator = new AtomicInteger(); 50 51 /** 52 * Staging area for requests that already have a duplicate request in flight. 53 * 54 *
    55 *
  • containsKey(cacheKey) indicates that there is a request in flight for the given cache 56 * key.
  • 57 *
  • get(cacheKey) returns waiting requests for the given cache key. The in flight request 58 * is not contained in that list. Is null if no requests are staged.
  • 59 *
60 */ 61 private final Map
>> mWaitingRequests = 62 new HashMap
>>(); 63 64 /** 65 * The set of all requests currently being processed by this RequestQueue. A Request 66 * will be in this set if it is waiting in any queue or currently being processed by 67 * any dispatcher. 68 */ 69 private final Set
> mCurrentRequests = new HashSet
>(); 70 71 /** The cache triage queue. */ 72 private final PriorityBlockingQueue
> mCacheQueue = 73 new PriorityBlockingQueue
>(); 74 75 /** The queue of requests that are actually going out to the network. */ 76 private final PriorityBlockingQueue
> mNetworkQueue = 77 new PriorityBlockingQueue
>(); 78 79 /** Number of network request dispatcher threads to start. */ 80 private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; 81 82 /** Cache interface for retrieving and storing responses. */ 83 private final Cache mCache; 84 85 /** Network interface for performing requests. */ 86 private final Network mNetwork; 87 88 /** Response delivery mechanism. */ 89 private final ResponseDelivery mDelivery; 90 91 /** The network dispatchers. */ 92 private NetworkDispatcher[] mDispatchers; 93 94 /** The cache dispatcher. */ 95 private CacheDispatcher mCacheDispatcher; 96 97 private List
mFinishedListeners = 98 new ArrayList
(); 99 100 /**101 * Creates the worker pool. Processing will not begin until {@link #start()} is called.102 *103 * @param cache A Cache to use for persisting responses to disk104 * @param network A Network interface for performing HTTP requests105 * @param threadPoolSize Number of network dispatcher threads to create106 * @param delivery A ResponseDelivery interface for posting responses and errors107 */108 public RequestQueue(Cache cache, Network network, int threadPoolSize,109 ResponseDelivery delivery) {110 mCache = cache;111 mNetwork = network;112 mDispatchers = new NetworkDispatcher[threadPoolSize];113 mDelivery = delivery;114 }115 116 /**117 * Creates the worker pool. Processing will not begin until {@link #start()} is called.118 *119 * @param cache A Cache to use for persisting responses to disk120 * @param network A Network interface for performing HTTP requests121 * @param threadPoolSize Number of network dispatcher threads to create122 */123 public RequestQueue(Cache cache, Network network, int threadPoolSize) {124 this(cache, network, threadPoolSize,125 new ExecutorDelivery(new Handler(Looper.getMainLooper())));126 }127 128 /**129 * Creates the worker pool. Processing will not begin until {@link #start()} is called.130 *131 * @param cache A Cache to use for persisting responses to disk132 * @param network A Network interface for performing HTTP requests133 */134 public RequestQueue(Cache cache, Network network) {135 this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);136 }137 138 /**139 * Starts the dispatchers in this queue.140 */141 public void start() {142 stop(); // Make sure any currently running dispatchers are stopped.143 // Create the cache dispatcher and start it.144 mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);145 mCacheDispatcher.start();146 147 // Create network dispatchers (and corresponding threads) up to the pool size.148 for (int i = 0; i < mDispatchers.length; i++) {149 NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,150 mCache, mDelivery);151 mDispatchers[i] = networkDispatcher;152 networkDispatcher.start();153 }154 }155 156 /**157 * Stops the cache and network dispatchers.158 */159 public void stop() {160 if (mCacheDispatcher != null) {161 mCacheDispatcher.quit();162 }163 for (int i = 0; i < mDispatchers.length; i++) {164 if (mDispatchers[i] != null) {165 mDispatchers[i].quit();166 }167 }168 }169 170 /**171 * Gets a sequence number.172 */173 public int getSequenceNumber() {174 return mSequenceGenerator.incrementAndGet();175 }176 177 /**178 * Gets the {@link Cache} instance being used.179 */180 public Cache getCache() {181 return mCache;182 }183 184 /**185 * A simple predicate or filter interface for Requests, for use by186 * {@link RequestQueue#cancelAll(RequestFilter)}.187 */188 public interface RequestFilter {189 public boolean apply(Request
request);190 }191 192 /**193 * Cancels all requests in this queue for which the given filter applies.194 * @param filter The filtering function to use195 */196 public void cancelAll(RequestFilter filter) {197 synchronized (mCurrentRequests) {198 for (Request
request : mCurrentRequests) {199 if (filter.apply(request)) {200 request.cancel();201 }202 }203 }204 }205 206 /**207 * Cancels all requests in this queue with the given tag. Tag must be non-null208 * and equality is by identity.209 */210 public void cancelAll(final Object tag) {211 if (tag == null) {212 throw new IllegalArgumentException("Cannot cancelAll with a null tag");213 }214 cancelAll(new RequestFilter() {215 @Override216 public boolean apply(Request
request) {217 return request.getTag() == tag;218 }219 });220 }221 222 /**223 * Adds a Request to the dispatch queue.224 * @param request The request to service225 * @return The passed-in request226 */227 public
Request
add(Request
request) {228 // Tag the request as belonging to this queue and add it to the set of current requests.229 request.setRequestQueue(this);230 synchronized (mCurrentRequests) {231 mCurrentRequests.add(request);232 }233 234 // Process requests in the order they are added.235 request.setSequence(getSequenceNumber());236 request.addMarker("add-to-queue");237 238 // If the request is uncacheable, skip the cache queue and go straight to the network.239 if (!request.shouldCache()) {240 mNetworkQueue.add(request);241 return request;242 }243 244 // Insert request into stage if there's already a request with the same cache key in flight.245 synchronized (mWaitingRequests) {246 String cacheKey = request.getCacheKey();247 if (mWaitingRequests.containsKey(cacheKey)) {248 // There is already a request in flight. Queue up.249 Queue
> stagedRequests = mWaitingRequests.get(cacheKey);250 if (stagedRequests == null) {251 stagedRequests = new LinkedList
>();252 }253 stagedRequests.add(request);254 mWaitingRequests.put(cacheKey, stagedRequests);255 if (VolleyLog.DEBUG) {256 VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);257 }258 } else {259 // Insert 'null' queue for this cacheKey, indicating there is now a request in260 // flight.261 mWaitingRequests.put(cacheKey, null);262 mCacheQueue.add(request);263 }264 return request;265 }266 }267 268 /**269 * Called from {@link Request#finish(String)}, indicating that processing of the given request270 * has finished.271 *272 *

Releases waiting requests for request.getCacheKey() if273 * request.shouldCache().

274 */275
void finish(Request
request) {276 // Remove from the set of requests currently being processed.277 synchronized (mCurrentRequests) {278 mCurrentRequests.remove(request);279 }280 synchronized (mFinishedListeners) {281 for (RequestFinishedListener
listener : mFinishedListeners) {282 listener.onRequestFinished(request);283 }284 }285 286 if (request.shouldCache()) {287 synchronized (mWaitingRequests) {288 String cacheKey = request.getCacheKey();289 Queue
> waitingRequests = mWaitingRequests.remove(cacheKey);290 if (waitingRequests != null) {291 if (VolleyLog.DEBUG) {292 VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",293 waitingRequests.size(), cacheKey);294 }295 // Process all queued up requests. They won't be considered as in flight, but296 // that's not a problem as the cache has been primed by 'request'.297 mCacheQueue.addAll(waitingRequests);298 }299 }300 }301 }302 303 public
void addRequestFinishedListener(RequestFinishedListener
listener) {304 synchronized (mFinishedListeners) {305 mFinishedListeners.add(listener);306 }307 }308 309 /**310 * Remove a RequestFinishedListener. Has no effect if listener was not previously added.311 */312 public
void removeRequestFinishedListener(RequestFinishedListener
listener) {313 synchronized (mFinishedListeners) {314 mFinishedListeners.remove(listener);315 }316 }317 }
RequestQueue

 

1.

其实RequestQueue里面有两个队列,一个我称为缓存队列mCacheQueue,一个称为网络队列mNetworkQueue

如果请求要求加入缓存队列(例如我们给request设置一个属性ShouldCache,然后提供set方法来设置),将会试图从硬盘缓存中获取数据,如果没有缓存,这个请求将被放入网络队列

如果请求不要求缓存,则直接加入网络队列。

加入队列以后,我们开启线程,从队列中取出请求。

可想而知,我们最好有一个线程CacheDispatcher从缓存队列中取,一个NetworkDispatcher从网络队列中取,然而网络请求往往大量,所以volley实际上有多个线程同时从网络队列中取出请求(这里涉及线程同步,volley使用PriorityBlockingQueue解决)

为什么要先建立几个线程,从队列中取,而不是每个request开启一个线程呢?这样做的好处是避免重复大量创建线程所带来的开销,另外由于所有的request都存在在一个RequestQueue里面,便于我们对request的管理,例如我们要关闭某个request,又或者我们请求了很多相同的request,对应这些操作,我们如果将request分散,是很难统一解决的,所以这样用类似线程池的思想,统一管理线程。

同时,这样做又会带来不利,因为实际请求线程的线程数目是固定的,意味着当request数目大于线程数目时,有的线程将被阻塞,造成效率下降,更多的问题,会在接下来的文章提到。

至于CacheDispatcher和NetworkDispatcher是怎么请求数据的呢?

对于NetworkDispatcher而言,必然是开启网络连接,然后获取数据的(例如url.openConnection),这是我们的常用实现,先不做详细解释(volley对这些实现进行了更详细的封装)

 

再来考虑,获得结果以后,我们怎么回调。

还是面向对象的思路,volley将响应结果封装成一个repsonse类(和request对应)

对应NetworkDispatcher而言,在它的run()方法里面,取得request以后,根据url请求数据,将数据封装成respsonse对象,再有一个分发器ResponseDelivery分发到对应的request

有人会问?解析到response以后,我们给request设计一个方法(例如将parseRespose(Respsonse respsonse))用于使用response,同时在这个方法内,回调监听器不就好了吗?为什么要多此一举,创建一个分发器呢?

原因是这样更灵活,但是还有一个重要的原因是,注意到我们回调,往往是在主线程中进行的(因为很可能要操作UI),如果我们在NetworkDispatcher(子线程)里面,直接回调,可能造成错误,这是ResponseDelivery存在的另外一个原因。

根据上面的结论,最后来看一张简单的流程图

 

根据流程分析,我们可以体会到,volley设计框架的基本思路,对比于我们简单的实现,volley的实现方式耦合更加松散,使用面向接口编程,同时使用更多组合方式而不是继承。使用了代理等设计模式,同时提高了线程的利用率。总之volley的架构设计又各种各样的好处。

我在这里介绍几个volley的功能,以及它考虑到的,而我们很可能没有考虑到的问题。这些问题或者说功能上的优势,会伴随着本专栏的深入让大家逐渐体会。

 


下面从源码角度看RequestQueue类,首先当然是属性

1 /**   2  * A request dispatch queue with a thread pool of dispatchers.   3  *    4  * Calling {@link #add(Request)} will enqueue the given Request for dispatch,   5  * resolving from either cache or network on a worker thread, and then delivering   6  * a parsed response on the main thread.   7  * 一个拥有线程池的请求队列   8  * 调用add()分发,将添加一个用于分发的请求   9  * worker线程从缓存或网络获取响应,然后将该响应提供给主线程  10  */   11 public class RequestQueue {   12    13     /**   14      * Callback interface for completed requests.  15      * 任务完成的回调接口   16      */   17     public static interface RequestFinishedListener
{ 18 /** Called when a request has finished processing. */ 19 public void onRequestFinished(Request
request); 20 } 21 22 /** 23 * Used for generating monotonically-increasing sequence numbers for requests. 24 * 使用原子类,记录队列中当前的请求数目 25 */ 26 private AtomicInteger mSequenceGenerator = new AtomicInteger(); 27 28 /** 29 * Staging area for requests that already have a duplicate request in flight.
30 * 等候缓存队列,重复请求集结map,每个queue里面都是相同的请求 31 *
    32 *
  • containsKey(cacheKey) indicates that there is a request in flight for the given cache 33 * key.
  • 34 *
  • get(cacheKey) returns waiting requests for the given cache key. The in flight request 35 * is not contained in that list. Is null if no requests are staged.
  • 36 *
37 * 如果map里面包含该请求的cachekey,说明已经有相同key的请求在执行 38 * get(cacheKey)根据cachekey返回对应的请求 39 */ 40 private final Map
>> mWaitingRequests = 41 new HashMap
>>(); 42 43 /** 44 * The set of all requests currently being processed by this RequestQueue. A Request 45 * will be in this set if it is waiting in any queue or currently being processed by 46 * any dispatcher. 47 * 队列当前拥有的所以请求的集合 48 * 请求在队列中,或者正被调度,都会在这个集合中 49 */ 50 private final Set
> mCurrentRequests = new HashSet
>(); 51 52 /** 53 * The cache triage queue. 54 * 缓存队列 55 */ 56 private final PriorityBlockingQueue
> mCacheQueue = 57 new PriorityBlockingQueue
>(); 58 59 /** 60 * The queue of requests that are actually going out to the network. 61 * 网络队列,有阻塞和fifo功能 62 */ 63 private final PriorityBlockingQueue
> mNetworkQueue = 64 new PriorityBlockingQueue
>(); 65 66 /** 67 * Number of network request dispatcher threads to start. 68 * 默认用于调度的线程池数目 69 */ 70 private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; 71 72 /** 73 * Cache interface for retrieving and storing responses. 74 * 缓存 75 */ 76 private final Cache mCache; 77 78 /** 79 * Network interface for performing requests. 80 * 执行请求的网络 81 */ 82 private final Network mNetwork; 83 84 /** Response delivery mechanism. */ 85 private final ResponseDelivery mDelivery; 86 87 /** 88 * The network dispatchers. 89 * 该队列的所有网络调度器 90 */ 91 private NetworkDispatcher[] mDispatchers; 92 93 /** 94 * The cache dispatcher. 95 * 缓存调度器 96 */ 97 private CacheDispatcher mCacheDispatcher; 98 99 /** 100 * 任务完成监听器队列 101 */ 102 private List
mFinishedListeners = 103 new ArrayList
();

属性很多,而且耦合的类也比较多,我挑重要的讲,这里大家只要先记住某个属性是什么就可以,至于它的具体实现我们先不管

 

1,首先看List<RequestFinishedListener> mFinishedListeners任务完成监听器队列,这个队列保留了很多监听器,这些监听器都是监听RequestQueue请求队列的,而不是监听单独的某个请求。RequestQueue中每个请求完成后,都会回调这个监听队列里面的所有监听器。这是RequestQueue的统一管理的体现。

2,AtomicInteger mSequenceGenerator原子类,对java多线程熟悉的朋友应该知道,这个是为了线程安全而创造的类,不了解的朋友,可以把它认识是int类型,用于记录当前队列中的请求数目

3,PriorityBlockingQueue<Request<?>> mCacheQueue缓存队列,用于存放向请求缓存的request,线程安全,有阻塞功能,也就是说当队列里面没有东西的时候,线程试图从队列取请求,这个线程就会阻塞

4,PriorityBlockingQueue<Request<?>> mNetworkQueue网络队列,用于存放准备发起网络请求的request,功能同上

5,CacheDispatcher mCacheDispatcher缓存调度器,继承了Thread类,本质是一个线程,这个线程将会被开启进入一个死循环,不断从mCacheQueue缓存队列取出请求,然后去缓存Cache中查找结果

6,NetworkDispatcher[] mDispatchers网络调度器数组,继承了Thread类,本质是多个线程,所以线程都将被开启进入死循环,不断从mNetworkQueue网络队列取出请求,然后去网络Network请求数据

7,Set<Request<?>> mCurrentRequests记录队列中的所有请求,也就是上面mCacheQueue缓存队列与mNetworkQueue网络队列的总和,用于统一管理

8,Cache mCache缓存对象,面向对象的思想,把缓存看成一个实体

9,Network mNetwork网络对象,面向对象的思想,把网络看成一个实体

10,ResponseDelivery mDelivery分发器,就是这个分发器,负责把响应发给对应的请求,分发器存在的意义之前已经提到了,主要是为了耦合更加送并且能在主线程中操作UI

11,Map<String, Queue<Request<?>>> mWaitingRequests等候缓存队列,重复请求集结map,每个queue里面都是相同的请求。为什么需要这个map呢?map的key其实是request的url,如果我们有多个请求的url都是相同的,也就是说请求的资源是相同的,volley就把这些请求放入一个队列,在用url做key将队列放入map中。

因为这些请求都是相同的,可以说结果也是相同的。那么我们只要获得一个请求的结果,其他相同的请求,从缓存中取就可以了。

所以等候缓存队列的作用就是,当其中的一个request获得响应,我们就将这个队列放入缓存队列mCacheQueue中,让这些request去缓存获取结果就好了。

这是volley处理重复请求的思路。

 

其实看懂上面的属性就可以了解RequestQueue类的作用,大家结合上面的属性,看一下流程图

 

ok,我们还是从构造函数开始看起吧

/**      * Creates the worker pool. Processing will not begin until {@link #start()} is called.      * 创建一个工作池,在调用start()方法以后,开始执行      * @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;//网络接口,用于执行http请求          mDispatchers = new NetworkDispatcher[threadPoolSize];//根据线程池大小,创建调度器数组          mDelivery = delivery;//一个分发接口,用于响应和错误      }        /**      * 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      */      public RequestQueue(Cache cache, Network network, int threadPoolSize) {          this(cache, network, threadPoolSize,                  new ExecutorDelivery(new Handler(Looper.getMainLooper())));      }

对于RequestQueue来说,必须有的参数是缓存,网络,分发器,网络线程的数目

 

对应上面的属性可以知道,原来这些东西都是外部传进来的,参照本专栏的开篇,可以知道,是在Volley这个类里面传进来的,同时在外部,我们也是通过Volley.newRequestQueue()方法来创建并且开启queue队列的。

 

紧接着来看start()方法,这个方法用于启动队列

1 /**  2      * Starts the dispatchers in this queue.  3      */   4     public void start() {   5         stop();  //保证当前所有运行的分发停止 Make sure any currently running dispatchers are stopped.   6         // Create the cache dispatcher and start it.   7         //创建新的缓存调度器,并且启动它   8         mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);   9         mCacheDispatcher.start();  10   11         // Create network dispatchers (and corresponding threads) up to the pool size.  12         //创建网络调度器,并且启动它们  13         for (int i = 0; i < mDispatchers.length; i++) {  14             NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  15                     mCache, mDelivery);  16             mDispatchers[i] = networkDispatcher;  17             networkDispatcher.start();  18         }

可以看到,所谓启动队列,就是创建了CacheDispatcher缓存调度器,和mDispatchers[]网络调度器数组,根据前面的介绍我们知道,它们都是线程,所以start()方法里面,其实就是调用了它们的start()方法。也就是说RequestQueue启动的本质,是这些调度器的启动,这些调度器启动以后,会进入死循环,不断从队列中取出request来进行数据请求。

 

由于Dispatcher调度器的数目有限(是根据我们给构造方法传入的参数threadPoolSize决定的),意味着Volley框架,同时在执行数据请求的线程数目是有限的,这样避免了重复创建线程所带来的开销,同时可能会带来效率的下降。

所以threadPoolSize对不同的应用,设置的大小大家不同,大家要根据自己项目实际情况,经过测试来确定这个值。

 

说完开启,我们再来看RequestQueue的关闭

1 /**  2      * Stops the cache and network dispatchers.  3      * 停止调度器(包括缓存和网络)  4      */   5     public void stop() {   6         if (mCacheDispatcher != null) {   7             mCacheDispatcher.quit();   8         }   9         for (int i = 0; i < mDispatchers.length; i++) {  10             if (mDispatchers[i] != null) {  11                 mDispatchers[i].quit();  12             }  13         }  14     }

对比开启,其实stop()的本质也是关闭所有的调度器,调用了它们的quit()方法,至于这个方法做的是什么,很容易想到,是把它们内部while循环的标志设成false

 

再来看add()方法,这方法用于将request加入队列,也是一个非常重要方法

1 /**  2      * Adds a Request to the dispatch queue.  3      * @param request The request to service  4      * @return The passed-in request  5      * 向请求队列添加请求  6      */   7     public 
Request
add(Request
request) { 8 // Tag the request as belonging to this queue and add it to the set of current requests. 9 request.setRequestQueue(this);//为请求设置其请求队列 10 synchronized (mCurrentRequests) { 11 mCurrentRequests.add(request); 12 } 13 14 // Process requests in the order they are added. 15 request.setSequence(getSequenceNumber());//设置请求序号 16 request.addMarker("add-to-queue"); 17 18 // If the request is uncacheable, skip the cache queue and go straight to the network. 19 //如果该请求不缓存,添加到网络队列 20 if (!request.shouldCache()) { 21 mNetworkQueue.add(request); 22 return request; 23 } 24 //如果该请求要求缓存 25 // Insert request into stage if there's already a request with the same cache key in flight. 26 synchronized (mWaitingRequests) { 27 String cacheKey = request.getCacheKey(); 28 if (mWaitingRequests.containsKey(cacheKey)) { 29 // There is already a request in flight. Queue up. 30 //如果已经有一个请求在工作,则排队等候 31 Queue
> stagedRequests = mWaitingRequests.get(cacheKey); 32 if (stagedRequests == null) { 33 stagedRequests = new LinkedList
>(); 34 } 35 stagedRequests.add(request); 36 mWaitingRequests.put(cacheKey, stagedRequests); 37 if (VolleyLog.DEBUG) { 38 VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); 39 } 40 } else { 41 // Insert 'null' queue for this cacheKey, indicating there is now a request in 42 // flight. 43 //为该key插入null,表明现在有一个请求在工作 44 mWaitingRequests.put(cacheKey, null); 45 mCacheQueue.add(request); 46 } 47 return request; 48 } 49 }

对于一个request而言,首先它会被加入mCurrentRequests,这是用于request的统一管理

 

然后,调用shouldCache()判断是从缓存中取还是网络请求,如果是网络请求,则加入mNetworkQueue,然后改方法返回

如果请求缓存,根据mWaitingRequests是否已经有相同的请求在进行,如果是,则将该request加入mWaitingRequests

如果不是,则将request加入mCacheQueue去进行缓存查询

 

到目前为止,我们知道了调度器会从队列里面拿请求,至于具体是怎么请求的,我们还不清楚。这也体现了volley设计的合理性,通过组合来分配各个职责,每个类的职责都比较单一。

我们提到,RequestQueue的一个重要作用,就是对request的统一管理,其实所谓的管理,更多是对request的关闭,下面我来看一下这些方法

1 /**  2      * Called from {@link Request#finish(String)}, indicating that processing of the given request  3      * has finished.  4      * 在request类的finish()方法里面,会调用这个方法,说明该请求结束  5      * 

Releases waiting requests for request.getCacheKey() if 6 * request.shouldCache().

7 */ 8 public
void finish(Request
request) { 9 // Remove from the set of requests currently being processed. 10 synchronized (mCurrentRequests) {
//从当前请求队列中移除 11 mCurrentRequests.remove(request); 12 } 13 synchronized (mFinishedListeners) {
//回调监听器 14 for (RequestFinishedListener
listener : mFinishedListeners) { 15 listener.onRequestFinished(request); 16 } 17 } 18 19 if (request.shouldCache()) {
//如果该请求要被缓存 20 synchronized (mWaitingRequests) { 21 String cacheKey = request.getCacheKey(); 22 Queue
> waitingRequests = mWaitingRequests.remove(cacheKey);//移除该缓存 23 if (waitingRequests != null) {
//如果存在缓存等候队列 24 if (VolleyLog.DEBUG) { 25 VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", 26 waitingRequests.size(), cacheKey); 27 } 28 // Process all queued up requests. They won't be considered as in flight, but 29 // that's not a problem as the cache has been primed by 'request'. 30 // 处理所有队列中的请求 31 mCacheQueue.addAll(waitingRequests);// 32 } 33 } 34 } 35 }

finish()用于表示某个特定的request完成了,只有将要完成的request传进来就好了,然后会在各个队列中移除它

 

这里需要注意,一个request完成以后,会将waitingRequests里面所有相同的请求,都加入到mCacheQueue缓存队列中,这就意味着,这些请求从缓存中取出结果就好了,这样就避免了频繁相同网络请求的开销。这也是Volley的亮点之一。

 

然后我们再来看一些取消方法

1 /**  2      * A simple predicate or filter interface for Requests, for use by  3      * {@link RequestQueue#cancelAll(RequestFilter)}.  4      * 一个简单的过滤接口,在cancelAll()方法里面被使用  5      */   6     public interface RequestFilter {   7         public boolean apply(Request
request); 8 } 9 10 /** 11 * Cancels all requests in this queue for which the given filter applies. 12 * @param filter The filtering function to use 13 * 根据过滤器规则,取消相应请求 14 */ 15 public void cancelAll(RequestFilter filter) { 16 synchronized (mCurrentRequests) { 17 for (Request
request : mCurrentRequests) { 18 if (filter.apply(request)) { 19 request.cancel(); 20 } 21 } 22 } 23 } 24 25 /** 26 * Cancels all requests in this queue with the given tag. Tag must be non-null 27 * and equality is by identity. 28 * 根据标记取消相应请求 29 */ 30 public void cancelAll(final Object tag) { 31 if (tag == null) { 32 throw new IllegalArgumentException("Cannot cancelAll with a null tag"); 33 } 34 cancelAll(new RequestFilter() { 35 @Override 36 public boolean apply(Request
request) { 37 return request.getTag() == tag; 38 } 39 }); 40 }

上面的设计可以说是非常巧妙的,为了增加取消的灵活性,创建了一个RequestFilter来自定义取消request的规则

 

在cancelAll(RequestFilter filter)方法里面,我们传入过滤器,就可以根据需要取消我想要取消的一类request,这种形式类似文件遍历的FileFilter

而这种形式,volley还为我们提供了一个具体的实现cancelAll(final Object tag),来根据标签取消request,这里我们也就明白了request<T>类中mTag属性的用处了

可以说volley处处都体现了设计模式的美感。

 

Ok,RequestQueue介绍到这里,就介绍了整个的基本结构,剩下的困惑,是CacheDispatcher,networkDispatcher怎么从队列里面取出request的问题了,但是这些问题跟队列的关系没有那么紧,也就是说具体实现的任务,又交到了这两个类的身上,总而言之,这里也体现了单一责任原则。

接下来的文章,将会分类讲述这两个功能的实现。

 

你可能感兴趣的文章
开源地图 Mapbox 收购健身数据追踪 App 供应商 Human
查看>>
《深入解析sas:数据处理、分析优化与商业应用》一第3章 对单个数据集的处理...
查看>>
小程序为何刚上线就遭冷落,部分已停止更新
查看>>
《Linux设备驱动开发详解 A》一一2.1 处理器
查看>>
传奇图形程序员Michael Abrash加盟Oculus与卡马克团聚
查看>>
《3D打印:正在到来的工业革命(第2版)》——1.7节21世纪的个人计算机
查看>>
苹果要求所有应用到 2016 年底必须使用 HTTPS
查看>>
R语言数据挖掘1.4 社交网络挖掘
查看>>
阿里感悟(十一)如何准备面试
查看>>
如何在终端下以后台模式运行Linux程序
查看>>
《Linux/UNIX OpenLDAP实战指南》——2.6 OpenLDAP目录树规划
查看>>
《循序渐进学Spark 》Spark 编程模型
查看>>
Linux集群和自动化维2.6.5 自动化类脚本
查看>>
《HTML5+CSS3网页设计入门必读》——2.3 错误处理
查看>>
Java 集合教程
查看>>
本文来自合作伙伴“阿里聚安全”.
查看>>
《面向机器智能的TensorFlow实践》一3.3 通过名称作用域组织数据流图
查看>>
《Android应用开发入门经典(第3版)》——第6.4节ProgressBar和SeekBar
查看>>
《iOS 6核心开发手册(第4版)》——导读
查看>>
CMS gc调整实践(续)
查看>>