Volley學習(RequestQueue分析),volleyrequestqueue
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 IMMEDIATE6 }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 in2 // 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, but17 // 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; } }); }