Volley Learning (RequestQueue analysis), volleyrequestqueue
Volley's RequestQueue is used to cache Request processor CacheDispatch and network Request processor NetworkDispatch for Request processing. When we call RequestQueue. start (), the two processors start to run and wait for the Request to arrive.
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 first reads the cache and then obtains it from the network without the cache hit. Therefore, start CacheDispatcher and then start NetworkDispatcher. However, before starting the processor, call the stop () function to clear the expired Dispatcher in the RequestQueue (the Dispatcher inherits the Thread ). To prevent impact on performance. Volley starts a CacheDispatcher and four NetworkDispatcher. In this design, the individual mainly considers the download of network images, so multiple NetworkDispatcher is used to process network requests. Then let's take a look at the stop () function.
public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (int i = 0; i < mDispatchers.length; i++) { if (mDispatchers[i] != null) { mDispatchers[i].quit(); } } }
Call the quit () function of Dispatcher to end the thread. Take NetworkDispatcher. quit () as an example:
public void quit() { mQuit = true; interrupt(); }
The function sets the mQuit variable to true. The reason for this is that, in the exception handling process of the networkdispatcher thread, the mQuit value is determined. If it is true, the loop is exited and the thread is terminated. Otherwise, continue continues to process the Request from the Queue.
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; }
Next, let's take a look at the add function of RequestQueue.
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; } }
First, add the Request to mCurrentRequests. Because there are multiple threads competing, the code block is synchronized. Then request. setSequence (). assign a serial number to the current Request. Why? Because we need to put the Request in NetworkQueue or CacheQueue. Both queues are PriorityBlockingQueue, the elements are sorted by custom weights. The elements in PriorityBlockingQueue must implement the Comparable interface. Let's look at the Requeset implementation here:
@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(); }
The Request policy is determined based on the Priority of each Request. If the Priority of the two requests is the same, the Sequence of the two requests is determined based on the Sequence of the two requests.
1 public enum Priority {2 LOW, 3 NORMAL, 4 HIGH, 5 IMMEDIATE6}View Code
After adding a sequence to the current Request, determine whether the current Request needs to be cached. If not, add the Request to the NetworkQueue queue directly. If you need to cache the Request, retrieve the Request cache key and check whether there are any Request cache keys from mWaitingRequests. There are four queues in RequestQueue. MCurrentRequests, mWaitingRequests, mCacheQueue, and mNetworkQueue. When a Request arrives, it is first added to mCurrentRequests and then determined whether the current Request needs to be cached. If no cached Request is required, it is directly added to the mNetworkQueue queue to wait for the network processor (NetWorkDispatcher) to process. If cache is required, obtain the corresponding cacheKey based on the Request. If the cacheKey does not exist, the Request to be cached is the first Request. Put the cacheKey in the mWaitingRequests queue. (In this example, mCurrentRequests stores all requests processed by RequestQueue, while mWaitingRequests stores requests with the same url in mCacheQueue, the emergence of mWatiingRequests is to avoid unnecessary network data acquisition), and put the Request into mCacheQueue for processing.
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
If the cacheKey exists, it indicates that the same Request is being processed (here, the cacheKey is obtained through getUrl (), that is, the url when the Request is created ). Put the Request into the mWaitingRequest queue and wait for the processing result of the In-flight Request.
After adding, check 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
First, remove the current Request from the mCurrentRequests collection, and then remove the current Request from mWaitingRequests. put the Request stored in the mWaitingRequest corresponding to this Request to mCacheQueue for medium processing (because the network data of the corresponding url has been loaded to the local device, therefore, the requests in these mWaitingRequests are directly parsed from the local machine and retrieved from the network without time consumption ).
The RequestQueue class also has a CancelAll () function, which is used to delete the response Request based on the specified Request tag.
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; } }); }