preface

Because the project that I’m in charge of now is going to be componentized. The first is to repackage a network library component based on the specific business of the project. The current project is using Volley, so before encapsulation, volley source code was read.

Volley parsing begins

Note: The source code read here is the version used in the project and may differ from the latest Volley code.

  • Start with Volley’s static method, newRequestQueue

  • Explain the class that appears in the newRequestQueue(Context Context, HttpSatck stack) method above:

    • HttpStack: an abstract interface protocol for implementing network requests, implementing classes such as HurlStack, can also be replaced with OkHttp.
    • Network: is an abstract interface protocol to execute Network requests. The concrete implementation class is BasicNetwork.
    • Cache: An abstract interface protocol for caching network request data. The concrete implementation class is DiskBasedCache.
    • RequestQueue: A network request dispatch queue with an array of threads. Next look at its queue.start() method, which enables a CacheDispatcher thread and creates NetworkDispatcher threads through a for loop.

  • Explain the classes that appear in the start() method above:

    • PriorityBlockingQueue: An unbounded blocking queue with a priority, so the Request abstract class implements the Comparable interface.
    • PriorityBlockingQueue-mCacheQueue: Cache request queue.
    • PriorityBlockingQueue-mNetworkQueue: Network request queue.
    • Cache-mcache: caches network request information to the local disk. The implementation class is DiskBasedCache.
    • ResponseDelivery-mDelivery: sends the response data of network requests to the abstract interface protocol of the business layer. The implementation class is ExecutorDelivery, and internally sends the data to the main thread through the Handler.
    • Request abstract class: mainly set the Request mode, process the Request data uploaded to the server, parse the Request results, etc. The specific implementation class is implemented by itself, of course, Volley also provides JsonObjectRequest, ImageRequest, etc.
  • The add method in the RequestQueue adds the request to the queue and then processes it, usually initiated by the business layer.

  • Explain the logic in the add method above
    • :

      Set<Request<? >> mCurrentRequests: adds Request to the collection of current requests. It is used to cancelAll network requests in the cancelAll method.
    • :

      Determines whether to add the request to the cache queue, or otherwise to the network request queue. Default is true.
    • :

      Map<String, Queue<Request<? >>> mWaitingRequests is a set of waiting requests. The key value is obtained via getCacheKey() in Request. The default value is a Url and can be overridden. Continue parsing the code at three points to determine whether mWaitingRequests contain the current request queue, if they do, add them to the queue to override previous requests, and if they do not, add them to the mCacheQueue queue and the mWaitingRequests collection.

  • Note the difference between the cancel, Finish, and stop methods in RequesetQueue
    • Cancel: a business layer call that does not return the result of the request.
    • Finish: This is called when the network request has completed, mainly after the end of the request condition has been met in NetworkDispatcher, and the internal operation has been done to empty the resource.
    • Stop: Cache distribution threads are interrupted by the threadinterrupt method. Network distribution threads are interrupted by the threadinterrupt method. Once this method is called, volley stops working and must be restarted by the start method.

In fact, when I was looking at this code I was wondering why it was all added to the cache queue, what about the network request queue, how did the network request get executed? Next, look at two important classes CacheDispatcher and NetworkDispatcher.

CacheDispatcher: inherits from a thread and executes a while loop in the run method that listens to the mCacheQueue (mCacheQueue).

@Override public void run() { if (DEBUG) VolleyLog.v("start new dispatcher"); / / set the priority of a thread Process. SetThreadPriority (Process. THREAD_PRIORITY_BACKGROUND); // Initialize DiskBaseCache mCache. Initialize (); While (true) {// This is an array of threads, not a real thread pool, and cannot dynamically create new threads. Here, too, a try catch must be used to catch exceptions. Try {// Fetch the request from the cache queue. > request = mCacheQueue.take(); request.addMarker("cache-queue-take"); // Finish if (request.iscanceled ()) {request.finish("cache-discard-canceled"); continue; } // Cache.entry = mCache. Get (request.getcacheKey ()); if (entry == null) { request.addMarker("cache-miss"); Mnetworkqueue.put (request); mnetworkQueue.put (request); continue; } // If (entry.isexpired ()) {request.addmarker ("cache-hit-expired"); // If (entry.isexpired ()) {request. request.setCacheEntry(entry); mNetworkQueue.put(request); continue; } // Request. AddMarker ("cache-hit"); Response<? > response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); request.addMarker("cache-hit-parsed"); if (! RefreshNeeded ()) {// Return mdelivery.postResponse (request, response) if the data source is not refreshed; } else {// To refresh the data, return the intermediate response data to the user, and then add the request to the network queue to execute the network request. request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry); response.intermediate = true; mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(request); } catch (InterruptedException e) { } } }); } } catch (InterruptedException e) { if (mQuit) { return; } continue; }}}Copy the code

NetworkDispatcher: inherits from the thread. It also starts a while loop in the run method, which keeps listening to the mNetworkQueue.

@ Override public void the run () {/ / set the thread priority Process. The setThreadPriority (Process. THREAD_PRIORITY_BACKGROUND); while (true) { long startTimeMs = SystemClock.elapsedRealtime(); Request<? > request; Try {// Fetch reuqest request = mqueue.take (); } catch (InterruptedException e) { if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take"); Request. IsCanceled () {request. Finish (" network-cancelled "); // Cancel immediately if (request.iscanceled ()) {request. continue; } addTrafficStatsTag(request);} addTrafficStatsTag(request); NetworkResponse NetworkResponse = mnetwork.performRequest (request); NetworkResponse = mnetwork.performRequest (request); request.addMarker("network-http-complete"); // If the server returns 304 or the response data has been sent, Just finish the if the request (networkResponse notModified && request. HasHadResponseDelivered ()) {request. Finish (" the not - modified "); continue; } // Parse the Response data Response<? > response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); If (request.shouldCache() && response.cacheEntry!) if (request.shouldCache() && response.cacheEntry! = null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } // Mark that the current request has sent the response data. Request.markdelivered (); // The response data is sent out and sent to the main thread via handler. mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); VolleyError volleyError = new VolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request, volleyError); }}}Copy the code

Volley analysis summary

  • Volley’s basic model: The CacheDispatcher thread and NetworkDispatcher thread listen to the corresponding cache queue and network request queue in an endless loop. When a request is about to be added to the queue, the cache is enabled. If it is not enabled, it is directly added to the network request queue for processing by the network distributor thread; otherwise, it is determined whether to add it to the network request queue for processing by the cache distributor thread. This is the basic flow, there may be some things that are not analyzed here, such as the byte array buffer pool ByteArrayPool, etc., mainly volley execution flow.
  • From the perspective of code encapsulation Volley: It makes a lot of interface protocols that only interact with the upper-layer interface, not the implementation class. The advantage is that the core code doesn’t change whether your network request is implemented by HttpClient, HttpUrlConnection, or OkHtpp. You can simply add classes that implement the HttpStack interface without affecting other classes, because adding is better than modifying, so when you encapsulate your own code, know how to specify and encapsulate our upper interface protocol, isolate specific implementation classes and logic code, and make your code maintainable and extensible.
  • Finally: if there is an analysis wrong place, welcome to point fingers and discuss.