Introduction of Volley

As we all know, Volley is an asynchronous asynchronous asynchronous HTTP network request library that Google opened source in 2013. It is very easy to use Volley for network requests. So what’s the asynchronous method? The simplest explanation is that with Volley you no longer need to use new threads (new runable()) like HttpUrlConnection; Open a new worker thread and Volley does all that for you. Volley provides the following guarantees for HTTP requests:

  1. The response cache. Volley maintains a cache to cache the results of network requests. The Volley accesses the cache first when sending a request again. If the request content is the same as the last one, the Volley directly returns data from the cache to avoid repeated network requests.
  2. Request request queue. Volley places requests added through the request.add() method in the request queue and fetches requests from the queue one by one. Request priority setting is supported.
  3. The response distribution. Volley adopts the asynchronous request mode. When the request is successful and the return value is obtained, the result will be parsed in the worker thread and the response will be distributed to the main thread. This means that in Volley’s callback method, the code is directly run in the main thread, which facilitates UI update and other operations. This is quite different from okHttp, where asynchronous callbacks still run on the worker thread. Each of these different implementations has its own benefits.
  4. Volley native Request includes JsonRequest, JsonArrayReuqest, ImageRequest and StringResquest. In addition to json objects, JSON arrays, and string return value requests, Volley allows you to implement your own requests from the Request class. For example, you can implement a Request that returns a JavaBean.

Volley is suitable for frequent but small network requests, such as common API calls, and is not suitable for downloading large files. Volley loads the entire response into memory and performs operations (such as parsing, etc.) on the large file. Volley may be mentioned on OOM website

Volley is easy to use

Using Volley in its simplest form involves three steps:

  1. Create RequestQueue
  2. Create the Request
  3. Add the Request to the RequestQueue

    RequestQueue requestQueue = Volley.newRequestQueue(context); StringRequest stringRequest = new StringRequest(Request.Method.GET, url, New Response.listener <String>() {@override public void onResponse(String Response) {// We can update UI}}, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); requestQueue.add(stringRequest);Copy the code

This article focuses on the analysis of Volley workflow, the specific use of various requests can refer to the following links

Analysis of Volley’s principle

Let’s start with a Volley workflow flowchart:



Volley includes the following threads:

  • Buffer read Thread (cacheDispatcher) – Used to fetch data from the buffer (inherited from Thread)
  • Network request Thread (networkDispatcher) – used to send network requests to fetch data (inherited from Thread)
  • Response distribution thread (mResponsePoster) – Used to distribute request results (possibly from cache and network) to the main thread (Executor object)

1. RequestQueue created

Since we first create a RequestQueue object with Volley, we’ll start with the volley.newRequestQueue (context) method. This static method creates a requestQueue object by internally calling the constructor of the overloaded static method:

public static RequestQueue newRequestQueue(Context context, File cacheDir = new File(context.getcachedir (), DEFAULT_CACHE_DIR); String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) {} if (stack == null) {//API9 and above use HttpUrlConnection if (build.version.sdk_int >= 9)  { stack = new HurlStack(); } else {// Prior to Gingerbread, HttpUrlConnection was unreliable. stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); Network Network = new BasicNetwork(stack); RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); Queue.start (); return queue; }Copy the code

As you can see from the above code, Volley internally uses HttpUrlConnection or HttpClient for network requests (HttpClient is deprecated and no longer maintained).

Let’s focus on this line of code:

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
Copy the code

The above code creates a new request queue. DiskBasedCache(cacheDir) is passed to initialize the cache for the corresponding directory. Tracing the above constructor, we find that another three-parameter constructor is called:

Public RequestQueue(Cache Cache, Network Network) {// Call this(Cache, Network, Network) constructor DEFAULT_NETWORK_THREAD_POOL_SIZE); }Copy the code

DEFAULT_NETWORK_THREAD_POOL_SIZE is the declared constant =4, so we know that Volley’s default number of network request threads is 4. The constructor above calls the constructor for four arguments:

Public RequestQueue(Cache Cache, Network Network, int threadPoolSize) {RequestQueue(Cache, Network, int threadPoolSize) { threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); }Copy the code

ExecutorDelivery: ExecutorDelivery: ExecutorDelivery: ExecutorDelivery: ExecutorDelivery: ExecutorDelivery: ExecutorDelivery: ExecutorDelivery: ExecutorDelivery: ExecutorDelivery

    public RequestQueue(Cache cache, Network network, int threadPoolSize,
        ResponseDelivery delivery) {
    mCache = cache;
    mNetwork = network;
    mDispatchers = new NetworkDispatcher[threadPoolSize];
    mDelivery = delivery;
}
Copy the code

So the requestQueue is created,

2. Network request execution

The created requestqueue.queue.start() method is called on the last line of creation. Queue.start () ¶

public void start() { stop(); // create cacheDispatcher and start mCacheDispatcher = new cacheDispatcher (mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); Create network request threads based on the size of the thread pool (mDispatchers). for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery); mDispatchers[i] = networkDispatcher; Networkdispatcher.start (); }}Copy the code

Just like the code comments, the cache thread is created and started to read the cache contents, and the network request thread is created for the network request. MDispatchers is an array of threads with a default size of 4. NetworkDispatcher requests execution threads. Let’s focus on the creation of network request threads:

NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery);
Copy the code

We’ll focus on the mDelivery object, which is an ExecutorDelivery object bundled with the main thread handler

Since NetworkDispatcher is itself a thread, calling start() actually executes its run() method, we go inside the run() method:

@Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); while (true) { long startTimeMs = SystemClock.elapsedRealtime(); Request<? > request; Request = mqueue.take (); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take"); If (request.iscanceled ()) {request.finish("network-discard-cancelled"); // check whether the request isCanceled. continue; } addTrafficStatsTag(request); // Send network requests NetworkResponse NetworkResponse = mnetwork.performRequest (request); . // Return result in child thread parsing Response<? > response = request.parseNetworkResponse(networkResponse); . The omitted code is used to cache the results of a successful request... // Send the result of the request to 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

More code, because the main research network request part, the above code request result cache part I have omitted, you can view the source code. The core of the above code is the following three methods:

mNetwork.performRequest() Response<? > response = request.parseNetworkResponse() mDelivery.postResponse(request, response);Copy the code

To avoid too much code I will not post source code, function:

  • Mnetwork.performrequest () — HurlStack().performRequest() is finally called to make a network request using httpUrlConnection.
  • Request. ParseNetworkResponse () – according to parseNetworkResponse specific logic implementation () in the child thread parse the results (this is also a custom request need to implement one of the methods, For example, if you want to return javaBean, you need to parse it in this method.)
  • Mdelivery.postresponse (request, response) — distributes the parsing results to the main thread

3. The distribution of the Response

Finally, let’s look at the distribution of Response. MDelivery is an ExecutorDelivery object, with an internal member called mResponsePoster(of Executor type). The mResponsePoster.execute() method is called to distribute the response. PostResponse (request, response, null) : postResponse(request, response, null) : postResponse(request, response, null);

public void postResponse(Request<? > request, Response<? > response) { postResponse(request, response, null); }Copy the code

The source code is as follows:

public void postResponse(Request<? > request, Response<? > response, Runnable runnable) { request.markDelivered(); request.addMarker("post-response"); // Execute mResponsePoster. Execute (new ResponseDeliveryRunnable(request, response, runnable)); }Copy the code

MResponsePoster is also an Executor object that executes the Runnable object’s run() method via the execute() method: its execute() method is implemented as follows:

ExecutorDelivery(ExecutorDelivery) {ExecutorDelivery(ExecutorDelivery) {ExecutorDelivery(ExecutorDelivery) {ExecutorDelivery(ExecutorDelivery) {ExecutorDelivery(ExecutorDelivery) {ExecutorDelivery(ExecutorDelivery) {ExecutorDelivery(ExecutorDelivery) {ExecutorDelivery(ExecutorDelivery) {ExecutorDelivery(ExecutorDelivery);  { @Override public void execute(Runnable command) { handler.post(command); }}; }Copy the code

You can see that handler.post() is used to make the Runnable object run in the thread to which this handler is bound. Where is this handler assigned? It is an ExecutorDelivery object that binds to the main thread handler, so our handler is tied to the main thread, and our callback will eventually run on the main thread when we distribute the response. ResponseDeliveryRunnable ();

If (mresponse.issuccess ()) {mrequest.deliverResponse (mresponse.result); } else { mRequest.deliverError(mResponse.error); }Copy the code

DeliverResponse (mResponse.result) is also a method that can be overridden when customizing a Request. In the StringRequest class that comes with Volley, this method is implemented as follows:

    @Override
protected void deliverResponse(String response) {
    mListener.onResponse(response);
}
Copy the code

This calls the onResponse(reponse) method of the listener we set when we created the StringRequest object using Volley. You can then update the UI in the change method.

Does Volley use thread pools?

It uses an array of threads (called mDispatcher) with a default length of 4. In the run() method of a specific network request thread (networkDispatcher), Volley uses the following structure:

Public void run(){while(true){// Request queue.take (); . }}Copy the code

This loop means that the run() method never terminates, the corresponding thread is not destroyed, and when requestqueue.take () does not receive a request (that is, the requestQueue is empty), the thread is blocked and temporarily stopped running. It gets run when there are new requests and sends new network requests. As you can see, Volley does not use a thread pool to manage network request threads. Instead, it uses an infinite loop to achieve the same effect as a thread pool, avoiding the overhead of constantly creating and destroying threads.

The last

Here is just a simple comb through the Volley network request process, the cache part did not make too much explanation, if you want to know more, also ask the reader to view the source code.

Alipay rewards