源码:

  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<T> {
  44. /** Called when a request has finished processing. */
  45. public void onRequestFinished(Request<T> 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. * <ul>
  55. * <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
  56. * key.</li>
  57. * <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
  58. * is <em>not</em> contained in that list. Is null if no requests are staged.</li>
  59. * </ul>
  60. */
  61. private final Map<String, Queue<Request<?>>> mWaitingRequests =
  62. new HashMap<String, Queue<Request<?>>>();
  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<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
  70.  
  71. /** The cache triage queue. */
  72. private final PriorityBlockingQueue<Request<?>> mCacheQueue =
  73. new PriorityBlockingQueue<Request<?>>();
  74.  
  75. /** The queue of requests that are actually going out to the network. */
  76. private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
  77. new PriorityBlockingQueue<Request<?>>();
  78.  
  79. /** Number of network request dispatcher threads to start. */
  80. private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = ;
  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<RequestFinishedListener> mFinishedListeners =
  98. new ArrayList<RequestFinishedListener>();
  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 disk
  104. * @param network A Network interface for performing HTTP requests
  105. * @param threadPoolSize Number of network dispatcher threads to create
  106. * @param delivery A ResponseDelivery interface for posting responses and errors
  107. */
  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 disk
  120. * @param network A Network interface for performing HTTP requests
  121. * @param threadPoolSize Number of network dispatcher threads to create
  122. */
  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 disk
  132. * @param network A Network interface for performing HTTP requests
  133. */
  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 = ; 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 = ; 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 by
  186. * {@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 use
  195. */
  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-null
  208. * 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. @Override
  216. 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 service
  225. * @return The passed-in request
  226. */
  227. public <T> Request<T> add(Request<T> 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<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
  250. if (stagedRequests == null) {
  251. stagedRequests = new LinkedList<Request<?>>();
  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 in
  260. // 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 request
  270. * has finished.
  271. *
  272. * <p>Releases waiting requests for <code>request.getCacheKey()</code> if
  273. * <code>request.shouldCache()</code>.</p>
  274. */
  275. <T> void finish(Request<T> 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<T> listener : mFinishedListeners) {
  282. listener.onRequestFinished(request);
  283. }
  284. }
  285.  
  286. if (request.shouldCache()) {
  287. synchronized (mWaitingRequests) {
  288. String cacheKey = request.getCacheKey();
  289. Queue<Request<?>> 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, but
  296. // that's not a problem as the cache has been primed by 'request'.
  297. mCacheQueue.addAll(waitingRequests);
  298. }
  299. }
  300. }
  301. }
  302.  
  303. public <T> void addRequestFinishedListener(RequestFinishedListener<T> 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 <T> void removeRequestFinishedListener(RequestFinishedListener<T> 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<T> {
  18. /** Called when a request has finished processing. */
  19. public void onRequestFinished(Request<T> 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.<br>
  30. * 等候缓存队列,重复请求集结map,每个queue里面都是相同的请求
  31. * <ul>
  32. * <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
  33. * key.</li>
  34. * <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
  35. * is <em>not</em> contained in that list. Is null if no requests are staged.</li>
  36. * </ul>
  37. * 如果map里面包含该请求的cachekey,说明已经有相同key的请求在执行
  38. * get(cacheKey)根据cachekey返回对应的请求
  39. */
  40. private final Map<String, Queue<Request<?>>> mWaitingRequests =
  41. new HashMap<String, Queue<Request<?>>>();
  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<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
  51.  
  52. /**
  53. * The cache triage queue.
  54. * 缓存队列
  55. */
  56. private final PriorityBlockingQueue<Request<?>> mCacheQueue =
  57. new PriorityBlockingQueue<Request<?>>();
  58.  
  59. /**
  60. * The queue of requests that are actually going out to the network.
  61. * 网络队列,有阻塞和fifo功能
  62. */
  63. private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
  64. new PriorityBlockingQueue<Request<?>>();
  65.  
  66. /**
  67. * Number of network request dispatcher threads to start.
  68. * 默认用于调度的线程池数目
  69. */
  70. private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = ;
  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<RequestFinishedListener> mFinishedListeners =
  103. new ArrayList<RequestFinishedListener>();

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

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,我们还是从构造函数开始看起吧

  1. /**
  2. * Creates the worker pool. Processing will not begin until {@link #start()} is called.
  3. * 创建一个工作池,在调用start()方法以后,开始执行
  4. * @param cache A Cache to use for persisting responses to disk
  5. * @param network A Network interface for performing HTTP requests
  6. * @param threadPoolSize Number of network dispatcher threads to create
  7. * @param delivery A ResponseDelivery interface for posting responses and errors
  8. */
  9. public RequestQueue(Cache cache, Network network, int threadPoolSize,
  10. ResponseDelivery delivery) {
  11. mCache = cache;//缓存,用于保留响应到硬盘
  12. mNetwork = network;//网络接口,用于执行http请求
  13. mDispatchers = new NetworkDispatcher[threadPoolSize];//根据线程池大小,创建调度器数组
  14. mDelivery = delivery;//一个分发接口,用于响应和错误
  15. }
  16.  
  17. /**
  18. * Creates the worker pool. Processing will not begin until {@link #start()} is called.
  19. *
  20. * @param cache A Cache to use for persisting responses to disk
  21. * @param network A Network interface for performing HTTP requests
  22. * @param threadPoolSize Number of network dispatcher threads to create
  23. */
  24. public RequestQueue(Cache cache, Network network, int threadPoolSize) {
  25. this(cache, network, threadPoolSize,
  26. new ExecutorDelivery(new Handler(Looper.getMainLooper())));
  27. }

对于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 = ; 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 = ; 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 <T> Request<T> add(Request<T> 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<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
  32. if (stagedRequests == null) {
  33. stagedRequests = new LinkedList<Request<?>>();
  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. * <p>Releases waiting requests for <code>request.getCacheKey()</code> if
  6. * <code>request.shouldCache()</code>.</p>
  7. */
  8. public <T> void finish(Request<T> 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<T> listener : mFinishedListeners) {
  15. listener.onRequestFinished(request);
  16. }
  17. }
  18.  
  19. if (request.shouldCache()) {//如果该请求要被缓存
  20. synchronized (mWaitingRequests) {
  21. String cacheKey = request.getCacheKey();
  22. Queue<Request<?>> 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的问题了,但是这些问题跟队列的关系没有那么紧,也就是说具体实现的任务,又交到了这两个类的身上,总而言之,这里也体现了单一责任原则。

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

volley4--RequestQueue的更多相关文章

  1. Android中关于Volley的使用(五)从RequestQueue开始来深入认识Volley

    在前面的几篇文章中,我们学习了如何用Volley去网络加载JSON数据,如何利用ImageRequest和NetworkImageView去网络加载数据,而关于Volley的使用,我们都是从下面一行代 ...

  2. Volley(二)—— 基本Request对象 & RequestQueue&请求取消

    详细解读Volley(一)—— 基本Request对象 & RequestQueue&请求取消 Volley它非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作, ...

  3. Volley自定义Request及使用单例封装RequestQueue

    一.自定义Request Volley的所有的请求的超类型是Resuest,所有我们常用的请求都是这个类的子类,那么我们自定义View肯定也是基于这个类的. 案例: package com.zhy.v ...

  4. Volley HTTP库系列教程(3)自定义RequestQueue和编写单例RequestQueue示例

    Setting Up a RequestQueue Previous  Next This lesson teaches you to Set Up a Network and Cache Use a ...

  5. Volley 框架解析(二)--RequestQueue核心解读

    主要围绕RequestQueue进行解读,它的两个请求队列CacheQueue.NetworkQueue是如何调用的,第一条请求的执行过程及如何处理重复请求?对RequestQueue及相关的类进行详 ...

  6. Volley源码分析(一)RequestQueue分析

    Volley源码分析 虽然在2017年,volley已经是一个逐渐被淘汰的框架,但其代码短小精悍,网络架构设计巧妙,还是有很多值得学习的地方. 第一篇文章,分析了请求队列的代码,请求队列也是我们使用V ...

  7. 详细解读Volley(一)—— 基本Request对象 & RequestQueue

    Volley它非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕.所以不建议用它去进行下载文件.加载大图的操作.有人可能会问,如 ...

  8. Android请求网络共通类——Hi_博客 Android App 开发笔记

    今天 ,来分享一下 ,一个博客App的开发过程,以前也没开发过这种类型App 的经验,求大神们轻点喷. 首先我们要创建一个Andriod 项目 因为要从网络请求数据所以我们先来一个请求网络的共通类. ...

  9. 异步编程 In .NET

    概述 在之前写的一篇关于async和await的前世今生的文章之后,大家似乎在async和await提高网站处理能力方面还有一些疑问,博客园本身也做了不少的尝试.今天我们再来回答一下这个问题,同时我们 ...

  10. ASP.NET是如何在IIS下工作的

    ASP.NET与IIS是紧密联系的,由于IIS6.0与IIS7.0的工作方式的不同,导致ASP.NET的工作原理也发生了相应的变化. IIS6(IIS7的经典模式)与IIS7的集成模式的不同 IIS6 ...

随机推荐

  1. Handover

    In brief, eNodeB select one MME based on IE: Relative MME Capacity in S1 Setup Response, S-GW and P- ...

  2. dubbo学习笔记:快速搭建

    搭建一个简单的dubbo服务 参考地址: dubbo官网:http://dubbo.apache.org/zh-cn/docs/user/references/registry/zookeeper.h ...

  3. Flask 数据库迁移

    在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库.最直接的方式就是删除旧表,但这样会丢失数据. 更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中. ...

  4. Windows下的VMware导入到Mac的VMware Function

    在windows下是以文件夹的形式存在的,但是在Mac下是以.vmwarevm为后缀的文件. 操作步骤: 把windows下的虚拟机整个文件夹拷贝到Mac,然后文件夹后面加上.vmwarevm. 然后 ...

  5. Hello Jexus(转并修改)

    一.关于 CentOS CentOS(Community ENTerprise Operating System)是Linux发行版之一,它是来自于Red Hat Enterprise Linux依照 ...

  6. WCF系列教程之客户端异步调用服务

    本文参考自http://www.cnblogs.com/wangweimutou/p/4409227.html,纯属读书笔记,加深记忆 一.简介 在前面的随笔中,详细的介绍了WCF客户端服务的调用方法 ...

  7. Spark 概念学习系列之Spark Core(十五)

    不多说,直接上干货! 最关键的是转换算子Transformations和缓存算子Actions. 主要是对RDD进行操作. RDD Objects  ->  Scheduler(DAGSched ...

  8. JavaScript设计模式-6.封装

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. Linux笔记-vim 配置

    本文是转载的,我用的ubuntu12.04在vim设置方面就是参考了本文,拿来分享给大家! ubuntu10.10中的设置方法: $cd /etc/vim $sudo cp vimrc vimrc.b ...

  10. 从父子组件的mounted钩子的同步执行与页面的异步渲染看nextTick的用法

    最近复习vue的时候遇到了一个很奇怪的问题,我们直接从实例中看: <div id="app"> <child ref="child">& ...