volley介绍04

-------------------------------------------------------------------------------

转载:http://blog.csdn.net/crazy__chen/article/details/46490635

-------------------------------------------------------------------------------

上一篇文章给大家说明了Request<T>的内部结构,对于这个类而言,volley让我们关注的主要请求获得响应以后,怎么根据自己的需要解析响应,然后在主线程中回调监听器的方法,至于是怎么获得响应的,线程又是怎么开启的,都跟Request无关。

前面已经提到,Request会放在队列里面等待线程的提取,RequestQueue类作为volley的核心类,可以说是连接请求与响应的桥梁。另外,RequestQueue类作为一个集合,还给我们统一管理请求带来更大的方便,这样的思想是很值得我们学习的。

android设计上,也有使用队列这种形式的设计,一个比较典型的例子,就是handler,loop,message的实现。具体大家可以参考这篇文章blog.csdn.net/crazy__chen/article/details/44889479

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

[java] view plain copy

  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. * Callback interface for completed requests.
  14. * 任务完成的回调接口
  15. */
  16. public static interface RequestFinishedListener<T> {
  17. /** Called when a request has finished processing. */
  18. public void onRequestFinished(Request<T> request);
  19. }
  20. /**
  21. * Used for generating monotonically-increasing sequence numbers for requests.
  22. * 使用原子类,记录队列中当前的请求数目
  23. */
  24. private AtomicInteger mSequenceGenerator = new AtomicInteger();
  25. /**
  26. * Staging area for requests that already have a duplicate request in flight.<br>
  27. * 等候缓存队列,重复请求集结map,每个queue里面都是相同的请求
  28. * <ul>
  29. *     <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
  30. *          key.</li>
  31. *     <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
  32. *          is <em>not</em> contained in that list. Is null if no requests are staged.</li>
  33. * </ul>
  34. * 如果map里面包含该请求的cachekey,说明已经有相同key的请求在执行
  35. * get(cacheKey)根据cachekey返回对应的请求
  36. */
  37. private final Map<String, Queue<Request<?>>> mWaitingRequests =
  38. new HashMap<String, Queue<Request<?>>>();
  39. /**
  40. * The set of all requests currently being processed by this RequestQueue. A Request
  41. * will be in this set if it is waiting in any queue or currently being processed by
  42. * any dispatcher.
  43. * 队列当前拥有的所以请求的集合
  44. * 请求在队列中,或者正被调度,都会在这个集合中
  45. */
  46. private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
  47. /**
  48. * The cache triage queue.
  49. * 缓存队列
  50. */
  51. private final PriorityBlockingQueue<Request<?>> mCacheQueue =
  52. new PriorityBlockingQueue<Request<?>>();
  53. /**
  54. * The queue of requests that are actually going out to the network.
  55. * 网络队列,有阻塞和fifo功能
  56. */
  57. private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
  58. new PriorityBlockingQueue<Request<?>>();
  59. /**
  60. * Number of network request dispatcher threads to start.
  61. * 默认用于调度的线程池数目
  62. */
  63. private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
  64. /**
  65. * Cache interface for retrieving and storing responses.
  66. * 缓存
  67. */
  68. private final Cache mCache;
  69. /**
  70. * Network interface for performing requests.
  71. * 执行请求的网络
  72. */
  73. private final Network mNetwork;
  74. /** Response delivery mechanism. */
  75. private final ResponseDelivery mDelivery;
  76. /**
  77. * The network dispatchers.
  78. * 该队列的所有网络调度器
  79. */
  80. private NetworkDispatcher[] mDispatchers;
  81. /**
  82. * The cache dispatcher.
  83. * 缓存调度器
  84. */
  85. private CacheDispatcher mCacheDispatcher;
  86. /**
  87. * 任务完成监听器队列
  88. */
  89. private List<RequestFinishedListener> mFinishedListeners =
  90. 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,我们还是从构造函数开始看起吧

[java] view plain copy

  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. * Creates the worker pool. Processing will not begin until {@link #start()} is called.
  18. *
  19. * @param cache A Cache to use for persisting responses to disk
  20. * @param network A Network interface for performing HTTP requests
  21. * @param threadPoolSize Number of network dispatcher threads to create
  22. */
  23. public RequestQueue(Cache cache, Network network, int threadPoolSize) {
  24. this(cache, network, threadPoolSize,
  25. new ExecutorDelivery(new Handler(Looper.getMainLooper())));
  26. }

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

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

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

[java] view plain copy

  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. // Create network dispatchers (and corresponding threads) up to the pool size.
  11. //创建网络调度器,并且启动它们
  12. for (int i = 0; i < mDispatchers.length; i++) {
  13. NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
  14. mCache, mDelivery);
  15. mDispatchers[i] = networkDispatcher;
  16. networkDispatcher.start();
  17. }
  18. }

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

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

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

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

[java] view plain copy

  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加入队列,也是一个非常重要方法

[java] view plain copy

  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. // Process requests in the order they are added.
  14. request.setSequence(getSequenceNumber());//设置请求序号
  15. request.addMarker("add-to-queue");
  16. // If the request is uncacheable, skip the cache queue and go straight to the network.
  17. //如果该请求不缓存,添加到网络队列
  18. if (!request.shouldCache()) {
  19. mNetworkQueue.add(request);
  20. return request;
  21. }
  22. //如果该请求要求缓存
  23. // Insert request into stage if there‘s already a request with the same cache key in flight.
  24. synchronized (mWaitingRequests) {
  25. String cacheKey = request.getCacheKey();
  26. if (mWaitingRequests.containsKey(cacheKey)) {
  27. // There is already a request in flight. Queue up.
  28. //如果已经有一个请求在工作,则排队等候
  29. Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
  30. if (stagedRequests == null) {
  31. stagedRequests = new LinkedList<Request<?>>();
  32. }
  33. stagedRequests.add(request);
  34. mWaitingRequests.put(cacheKey, stagedRequests);
  35. if (VolleyLog.DEBUG) {
  36. VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
  37. }
  38. } else {
  39. // Insert ‘null‘ queue for this cacheKey, indicating there is now a request in
  40. // flight.
  41. //为该key插入null,表明现在有一个请求在工作
  42. mWaitingRequests.put(cacheKey, null);
  43. mCacheQueue.add(request);
  44. }
  45. return request;
  46. }
  47. }

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

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

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

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

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

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

[java] view plain copy

  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. if (request.shouldCache()) {//如果该请求要被缓存
  19. synchronized (mWaitingRequests) {
  20. String cacheKey = request.getCacheKey();
  21. Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);//移除该缓存
  22. if (waitingRequests != null) {//如果存在缓存等候队列
  23. if (VolleyLog.DEBUG) {
  24. VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
  25. waitingRequests.size(), cacheKey);
  26. }
  27. // Process all queued up requests. They won‘t be considered as in flight, but
  28. // that‘s not a problem as the cache has been primed by ‘request‘.
  29. // 处理所有队列中的请求
  30. mCacheQueue.addAll(waitingRequests);//
  31. }
  32. }
  33. }
  34. }

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

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

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

[java] view plain copy

  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. * Cancels all requests in this queue for which the given filter applies.
  11. * @param filter The filtering function to use
  12. * 根据过滤器规则,取消相应请求
  13. */
  14. public void cancelAll(RequestFilter filter) {
  15. synchronized (mCurrentRequests) {
  16. for (Request<?> request : mCurrentRequests) {
  17. if (filter.apply(request)) {
  18. request.cancel();
  19. }
  20. }
  21. }
  22. }
  23. /**
  24. * Cancels all requests in this queue with the given tag. Tag must be non-null
  25. * and equality is by identity.
  26. * 根据标记取消相应请求
  27. */
  28. public void cancelAll(final Object tag) {
  29. if (tag == null) {
  30. throw new IllegalArgumentException("Cannot cancelAll with a null tag");
  31. }
  32. cancelAll(new RequestFilter() {
  33. @Override
  34. public boolean apply(Request<?> request) {
  35. return request.getTag() == tag;
  36. }
  37. });
  38. }

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

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

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

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

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

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

时间: 03-21

volley介绍04的相关文章

volley介绍06

----------------------------------------------------------------------------- 转载:http://blog.csdn.net/crazy__chen/article/details/46506921 ----------------------------------------------------------------------------- 上一篇文章当中,我介绍了CacheDispatcher和缓存类Ca

volley介绍03

------------------------------------------------------------------------------ 转载:http://blog.csdn.net/crazy__chen/article/details/46486123 ------------------------------------------------------------------------------ 在上一篇文章中,我们已经提到volley的使用方式和设计的整体

volley介绍08

----------------------------------------------------------------------------------- 转载:http://blog.csdn.net/crazy__chen/article/details/46612901 ----------------------------------------------------------------------------------- 在上篇文章中,我们最终通过网络,获取到了H

volley介绍05

---------------------------------------------------------------------------------- 转载:http://blog.csdn.net/crazy__chen/article/details/46494627 ---------------------------------------------------------------------------------- 从上一篇文章我们已经知道,现在要处理的问题就是

GoogleIO 2013 Android快速联网框架Volley介绍

最近调研Android开发框架,看了xutils,KjFramework等框架的HTTP模块,觉得都太简单了,只是简单封装了HttpUrlConnenction和Handler,加了个回调函数,感觉就是比自己写一个好一点点.后面发现了Volley这个比较靠谱的Android网络请求框架,就用它了. Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮. 这是Volley名称的由来: a burst or emission of many things or a lar

WLAN频段介绍-04

ISM频段 ISM频段,此频段主要是开放给工业.科学.医学三个主要机构使用,该频段是依据美国联邦通讯委员会(FCC)所定义出来,并没有所谓使用授权的限制. 工业频段:美国频段为902-928MHz,欧洲900MHz的频段则有部份用于GSM通信.工业频段的引入避免了2.4GHz 附近各种无线通信设备的相互干扰. 科学频段:2.4GHz为各国共同的ISM频段.因此无线局域网,蓝牙,ZigBee等无线网络,均可工作在2.4GHz频段上,2.4GHz频段范围为2.4-2.4835GHz. 医疗频段:频段

Android Volley入门到精通:初识Volley的基本用法

1. Volley简介 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高. 不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码.于是乎,一些Android

【Android高级】应用开发必须要掌握的框架&lt;Volley&gt;

开发久了,就会发现掌握一个好的应用框架是多么的重要,虽然是别人的东西,你也许不能完全搞懂其中的原理,但你知道如何利用其到自己的开发中,这样不仅能节省大量的时间,而且别人沉淀下来的精华效果一定比他的厉害之处.Volley就是一个这么好的一个东西,发现两个大神总结的太好,我再总结就感觉造次了,详解如下所示: 1.Volley介绍 2.Volley用法 版权声明:本文为博主原创文章,未经博主允许不得转载.

volley 讲解

总结的不错的Volley介绍: Volley主页 https://android.googlesource.com/platform/frameworks/volley http://www.youtube.com/watch?v=yhv8l9F44qo&feature=player_embedded 1. 什么是Volley 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClien