zoukankan      html  css  js  c++  java
  • Volley学习(RequestQueue分析)

          Volley的RequestQueue用来缓存请求处理器CacheDispatch和网络请求处理器NetworkDispatch来处理Request的。当我们调用RequestQueue.start()是,两个处理器开始运行起来,等待Request的到来。

           

     public void start() {
            stop();  // Make sure any currently running dispatchers are stopped.
            // Create the cache dispatcher and start it.
            mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
            mCacheDispatcher.start();
    
            // Create network dispatchers (and corresponding threads) up to the pool size.
            for (int i = 0; i < mDispatchers.length; i++) {
                NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                        mCache, mDelivery);
                mDispatchers[i] = networkDispatcher;
                networkDispatcher.start();
            }
        }
    

      Volley先读缓存然后,没有cache hit的话再从网络上获取,所以先启动CacheDispatcher,然后启动NetworkDispatcher。不过在启动处理器前先调用stop()函数清除掉以前RequestQueue里的过期的Dispatcher(Dispatcher都是继承Thread)。以防影响性能。Volley启动一个CacheDispatcher和4个NetworkDispatcher,之所以这样设计,个人人为是主要考虑到网络图片的下载,所以利用多个NetworkDispatcher来处理网络请求。然后看一下stop()函数。

       

       public void stop() {
            if (mCacheDispatcher != null) {
                mCacheDispatcher.quit();
            }
            for (int i = 0; i < mDispatchers.length; i++) {
                if (mDispatchers[i] != null) {
                    mDispatchers[i].quit();
                }
            }
        }
    

       调用Dispatcher的quit()函数来结束线程。以NetworkDispatcher.quit()为例:

     public void quit() {
            mQuit = true;
            interrupt();
        }
    

      函数将mQuit变量置为true。为什么要这样做,因为在networkdispatcher线程中的中断异常处理中,判断mQuit的值,如果真,则退出循环,结束线程。否则continue,继续从Queue中去取Request处理。

     try {
                    // Take a request from the queue.
                    request = mQueue.take();
                } catch (InterruptedException e) {
                    // We may have been interrupted because it was time to quit.
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
    

      接下来,看下RequestQueue的add函数。

       

     public Request add(Request request) {
            // Tag the request as belonging to this queue and add it to the set of current requests.
            request.setRequestQueue(this);
            synchronized (mCurrentRequests) {
                mCurrentRequests.add(request);
            }
    
            // Process requests in the order they are added.
            request.setSequence(getSequenceNumber());
            request.addMarker("add-to-queue");
    
            // If the request is uncacheable, skip the cache queue and go straight to the network.
            if (!request.shouldCache()) {
                mNetworkQueue.add(request);
                return request;
            }
    
            // Insert request into stage if there's already a request with the same cache key in flight.
            synchronized (mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                if (mWaitingRequests.containsKey(cacheKey)) {
                    // There is already a request in flight. Queue up.
                    Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
                    if (stagedRequests == null) {
                        stagedRequests = new LinkedList<Request>();
                    }
                    stagedRequests.add(request);
                    mWaitingRequests.put(cacheKey, stagedRequests);
                    if (VolleyLog.DEBUG) {
                        VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                    }
                } else {
                    // Insert 'null' queue for this cacheKey, indicating there is now a request in
                    // flight.
                    mWaitingRequests.put(cacheKey, null);
                    mCacheQueue.add(request);
                }
                return request;
            }
        }
    

      首先将Request加入到mCurrentRequests中,因为存在多个线程竞争的问题,在这个代码块上进行了同步。然后request.setSequence().为当前Request分配一个序列号,为什么这样做,因为我们下面要将Request放到NetworkQueue中或者CacheQueue中,这两个队列都是PriorityBlockingQueue,里面的元素是根据自定义的权重来排序的。PriorityBlockingQueue里的元素须实现Comparable接口,来看下我们这里的Requeset的实现:

        

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

      Request的策略是现根据每个Request的Priority来判断,如果两个Request的Priority相同,那么载根据两个Request的Sequence来进行判断队列里的先后顺序。

         

    1 public enum Priority {
    2         LOW,
    3         NORMAL,
    4         HIGH,
    5         IMMEDIATE
    6     }
    View Code

      

         给当前Request加上序列后,判断一下当前Request是否需要缓存,如果不需要则直接把Request加入到NetworkQueue队列里。如果需要缓存,取出Request的缓存键,从mWaitingRequests里看下有没有Request的缓存键.在RequestQueue中有四个队列。mCurrentRequests,mWaitingRequests,mCacheQueue,mNetworkQueue。每当一个请求到来时,先加入到mCurrentRequests,然后判断当前Request是否需要缓存,如果不用缓存的Request,则直接加入到mNetworkQueue队列中等待网络处理器(NetWorkDispatcher)去处理。如果需要缓存的话,根据Request获取相应的cacheKey,如果cacheKey不存在的话,说明这个需要缓存的Request是第一次请求。那么将cacheKey放入到mWaitingRequests队列里。(这里插播一下,mCurrentRequests存放的是所有交由RequestQueue处理的Request,mWaitingRequests里存放的是mCacheQueue里已经有相同url的Request,mWatiingRequests的出现就是为了避免不必要的网络数据获取),并将Request放入到mCacheQueue中以做处理。

    1   // Insert 'null' queue for this cacheKey, indicating there is now a request in
    2                 // flight.
    3                 mWaitingRequests.put(cacheKey, null);
    4                 mCacheQueue.add(request);
    View Code

        如果cacheKey存在的话,说明已经有相同的Request正在处理(这里的cacheKey是通过getUrl()得到的,也就是创建Request时的url)。这时将此Request放入到mWaitingRequest队列中等待In-flight Request的处理结果。

        add完然后看finish(Request req);

     1  void finish(Request request) {
     2         // Remove from the set of requests currently being processed.
     3         synchronized (mCurrentRequests) {
     4             mCurrentRequests.remove(request);
     5         }
     6 
     7         if (request.shouldCache()) {
     8             synchronized (mWaitingRequests) {
     9                 String cacheKey = request.getCacheKey();
    10                 Queue<Request> waitingRequests = mWaitingRequests.remove(cacheKey);
    11                 if (waitingRequests != null) {
    12                     if (VolleyLog.DEBUG) {
    13                         VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
    14                                 waitingRequests.size(), cacheKey);
    15                     }
    16                     // Process all queued up requests. They won't be considered as in flight, but
    17                     // that's not a problem as the cache has been primed by 'request'.
    18                     mCacheQueue.addAll(waitingRequests);
    19                 }
    20             }
    21         }
    22     }
    View Code

         首先先从mCurrentRequests集合中remove掉当前Request,然后在mWaitingRequests中去掉当前的Request.然后将此Request对应的mWaitingRequest中存储的Request放到mCacheQueue中等待处理(因为此时对应的url的网络数据已经加载到本地,所以这些mWaitingRequests里的Request被处理时直接从本地解析,不用耗时的网络获取一遍)。

        RequestQueue类中还有一个CancelAll()函数,它的作用是根据指定的Request tag来删除响应的Request.

        public void cancelAll(RequestFilter filter) {
            synchronized (mCurrentRequests) {
                for (Request<?> request : mCurrentRequests) {
                    if (filter.apply(request)) {
                        request.cancel();
                    }
                }
            }
        }
    
        /**
         * Cancels all requests in this queue with the given tag. Tag must be non-null
         * and equality is by identity.
         */
        public void cancelAll(final Object tag) {
            if (tag == null) {
                throw new IllegalArgumentException("Cannot cancelAll with a null tag");
            }
            cancelAll(new RequestFilter() {
                @Override
                public boolean apply(Request<?> request) {
                    return request.getTag() == tag;
                }
            });
        }
    

      

  • 相关阅读:
    PAT 1097. Deduplication on a Linked List (链表)
    PAT 1096. Consecutive Factors
    PAT 1095. Cars on Campus
    PAT 1094. The Largest Generation (层级遍历)
    PAT 1093. Count PAT's
    PAT 1092. To Buy or Not to Buy
    PAT 1091. Acute Stroke (bfs)
    CSS:word-wrap/overflow/transition
    node-webkit中的requirejs报错问题:path must be a string error in Require.js
    script加载之defer和async
  • 原文地址:https://www.cnblogs.com/mKaoree/p/4471312.html
Copyright © 2011-2022 走看看