标签: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 }
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的功能,以及它考虑到的,而我们很可能没有考虑到的问题。这些问题或者说功能上的优势,会伴随着本专栏的深入让大家逐渐体会。
标签:insert size identity for 简单 重要 技术分享 响应 体会
原文地址:http://www.cnblogs.com/ganchuanpu/p/7627160.html