码迷,mamicode.com
首页 > 其他好文 > 详细

volley4--RequestQueue

时间:2017-10-04 21:37:30      阅读:276      评论:0      收藏:0      [点我收藏+]

标签:insert   size   identity   for   简单   重要   技术分享   响应   体会   

源码:

技术分享
  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的功能,以及它考虑到的,而我们很可能没有考虑到的问题。这些问题或者说功能上的优势,会伴随着本专栏的深入让大家逐渐体会。

volley4--RequestQueue

标签:insert   size   identity   for   简单   重要   技术分享   响应   体会   

原文地址:http://www.cnblogs.com/ganchuanpu/p/7627160.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!