• volley4--RequestQueue


    源码:

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

    /** 
         * 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 <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的问题了,但是这些问题跟队列的关系没有那么紧,也就是说具体实现的任务,又交到了这两个类的身上,总而言之,这里也体现了单一责任原则。

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

  • 相关阅读:
    epoll精髓【epoll】
    linux下调试core的命令,察看堆栈状态命令 摘录(http://blog.csdn.net/yearn520/article/details/6663265)
    使用epoll 在 linux 上开发高性能应用服务器【epoll】
    linux下epoll如何实现高效处理百万句柄的[转]【epoll】
    log4cplus入门
    非阻塞式服务器和客户端程序(TCP)【简单的原理例子】
    Linux有用的命令记录
    在Linux上的使用开源C++日志库log4cplus
    静态库和动态库的区别
    localtime多线下不安全,localtime_r线程安全
  • 原文地址:https://www.cnblogs.com/ganchuanpu/p/7627160.html
Copyright © 2020-2023  润新知