Volley (3), an open-source framework of android Network-Request queue is the main line

Source: Internet
Author: User

 

 

1. Where to start:

Starting from where, cainiao can think of the first place to come in touch ~~

First, let's look at the volley. java class:

 

    /**     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.     *     * @param context A {@link Context} to use for creating the cache dir.     * @param stack An {@link HttpStack} to use for the network, or null for default.     * @return A started {@link RequestQueue} instance.     */    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {        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) {            if (Build.VERSION.SDK_INT >= 9) {                stack = new HurlStack();            } else {                // Prior to Gingerbread, HttpUrlConnection was unreliable.                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }        Network network = new BasicNetwork(stack);        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);        queue.start();        return queue;    }
Recall that we used it to get our request queue. In the description of newRequestQueue, we also got the verification: Create a working pool (with a priority blocking Queue) instance. Here there are two static constructor methods. Apart from the one we used before, the other has an additional parameter HttpStack (which will be analyzed later by HttpStack ). We didn't use the constructor with HttpStack before. Therefore, the previous judgment on stack = null is as follows: different instances are created based on different versions. The cause comments are also clearly written: HttpUrlConnection is unavailable before Gingerbread (2.3. Here, we naturally think of the question: What is the difference between the previous and later versions of Gingerbread (2.3) in network requests (or the difference between HTTP Client and HttpURLConnection )? If you cannot open the web page, you can download the original English version or see the translation of Daniel. After the HttpStack is created, A Network and RequestQueue object are created: Network to execute Network requests; RequestQueue is a request dispatch queue with A thread pool of dispatchers. Here we will expand around RequestQueue. By the way, there is also an HttpStack interface, which has only one method. Here is a brief introduction:

 

public interface HttpStack {    /**     * Performs an HTTP request with the given parameters.     *     * 

A get request is sent if request. getPostBody () = null. a post request is sent otherwise, * and the Content-Type header is set to request. getPostBodyContentType ().

** @ Param request the request to perform * @ param additionalHeaders additional headers to be sent together with * {@ link Request # getHeaders ()} * @ return the HTTP response */public HttpResponse initiate mrequest (Request Request, Map AdditionalHeaders) throws IOException, AuthFailureError ;} Use the given parameters to execute the HTTP request. Through the above judgment, we know that there are two implementation classes: HurlStack and HttpClientStack. In this way, we can obtain the following relationship diagram:

 

In this way, their relationship is quite clear.

 

2. Request queue: RequestQueue depends on the key constructor:
   /**     * Creates the worker pool. Processing will not begin until {@link #start()} is called.     *     * @param cache A Cache to use for persisting responses to disk     * @param network A Network interface for performing HTTP requests     * @param threadPoolSize Number of network dispatcher threads to create     * @param delivery A ResponseDelivery interface for posting responses and errors     */    public RequestQueue(Cache cache, Network network, int threadPoolSize,            ResponseDelivery delivery) {        mCache = cache;        mNetwork = network;        mDispatchers = new NetworkDispatcher[threadPoolSize];        mDelivery = delivery;    }
Here we pass in a cache, Network, thread pool size, and response object. The default thread pool size is 4. After creating this RequestQueue object, we need to call its start () method to complete the initialization of the scheduler, for example, create a number of Dispatcher based on the size of the input thread pool (Here we only focus on the network processing thread), and then start:
    /**     * Starts the dispatchers in this queue.     */    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();        }    }
The NetworkDispatcher constructor imports an mNetworkQueue object, which is a congestion queue with priority:
/** The queue of requests that are actually going out to the network. */private final PriorityBlockingQueue
 
   mNetworkQueue = new PriorityBlockingQueue
  
   ();
  
 
Another concern is the add method:
/**     * Adds a Request to the dispatch queue.     * @param request The request to service     * @return The passed-in request     */    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
 
   stagedRequests = mWaitingRequests.get(cacheKey);                if (stagedRequests == null) {                    stagedRequests = new LinkedList
  
   ();                }                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;        }    }
  
 
The above Code shows that volley directly adds the Request object we created to the above mNetworkQueue. In this case, we naturally think that the request processing should be completed by the NetworkDispatcher created in start. NetworkDispatcher inherits the Thread and the run () method is implemented as follows:
 public void run() {        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        Request request;        while (true) {            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;            }            try {                request.addMarker(network-queue-take);                // If the request was cancelled already, do not perform the                // network request.                if (request.isCanceled()) {                    request.finish(network-discard-cancelled);                    continue;                }                // Tag the request (if API >= 14)                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {                    TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());                }                // Perform the network request.                NetworkResponse networkResponse = mNetwork.performRequest(request);                request.addMarker(network-http-complete);                // If the server returned 304 AND we delivered a response already,                // we're done -- don't deliver a second identical response.                if (networkResponse.notModified && request.hasHadResponseDelivered()) {                    request.finish(not-modified);                    continue;                }                // Parse the response here on the worker thread.                Response
  response = request.parseNetworkResponse(networkResponse);                request.addMarker(network-parse-complete);                // Write to cache if applicable.                // TODO: Only update cache metadata instead of entire record for 304s.                if (request.shouldCache() && response.cacheEntry != null) {                    mCache.put(request.getCacheKey(), response.cacheEntry);                    request.addMarker(network-cache-written);                }                // Post the response back.                request.markDelivered();                mDelivery.postResponse(request, response);            } catch (VolleyError volleyError) {                parseAndDeliverNetworkError(request, volleyError);            } catch (Exception e) {                VolleyLog.e(e, Unhandled exception %s, e.toString());                mDelivery.postError(request, new VolleyError(e));            }        }    }
First, retrieve a request from the request queue. Then, start to execute the request through the Network. At this time, the returned results will be obtained. Then, the results of the request will be returned to the request for processing:
Response
  response = request.parseNetworkResponse(networkResponse);
After the request is processed, a response is returned, and the response and request are finally passed to ResponseDeliveryRunnable for processing:
   /**     * A Runnable used for delivering network responses to a listener on the     * main thread.     */    @SuppressWarnings(rawtypes)    private class ResponseDeliveryRunnable implements Runnable {        private final Request mRequest;        private final Response mResponse;        private final Runnable mRunnable;        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {            mRequest = request;            mResponse = response;            mRunnable = runnable;        }        @SuppressWarnings(unchecked)        @Override        public void run() {            // If this request has canceled, finish it and don't deliver.            if (mRequest.isCanceled()) {                mRequest.finish(canceled-at-delivery);                return;            }            // Deliver a normal response or error, depending.            if (mResponse.isSuccess()) {                mRequest.deliverResponse(mResponse.result);            } else {                mRequest.deliverError(mResponse.error);            }            // If this is an intermediate response, add a marker, otherwise we're done            // and the request can be finished.            if (mResponse.intermediate) {                mRequest.addMarker(intermediate-response);            } else {                mRequest.finish(done);            }            // If we have been provided a post-delivery runnable, run it.            if (mRunnable != null) {                mRunnable.run();            }       }    }
Here we can see that if the request response is successful, the request's deliverResponse will be called, so that a complete request response process is completed. You may have noticed that the request is called in many places from the add () method of RequestQueue to the run () method of NetworkDispatcher. addMarker (**-**); In this method, we can easily understand the entire processing process through the strings in it: add-to-queue, network-queue-take, network-http-complete, network-parse-complete, network-cache-written, post-response/post-error, intermediate- response, there is also request. finish (***-***); this can also help us understand the entire processing process.
For example (I want to modify this image, but I don't know how to modify it. It is all dragged out ).
3. request details start with the run () of NetworkDispatcher (there is a post above the Code ).
3.1 first Cancellation
                request.addMarker(network-queue-take);                // If the request was cancelled already, do not perform the                // network request.                if (request.isCanceled()) {                    request.finish(network-discard-cancelled);                    continue;                }
The status is just retrieved from the queue: network-queue-take. First, determine whether the request has been canceled. If the request is canceled, call the finish () method of the request directly. At this time, we will think, how do we know that the request has been canceled or how can the user cancel the request? In the request, we can find a cancel () method to cancel the request. To deregister this method, we can see that this method is called in RequestQueue. They are:
    /**     * Cancels all requests in this queue for which the given filter applies.     * @param filter The filtering function to use     */    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;            }        });    }
In this way, we can know that the cancelAll () method in RequestQueue can be used to cancel a request. At this time, our request will not be sent. Of course, cancellation will occur at any time. We will also discuss below that the cancellation result will not be passed to the user after the request is returned.

3.2 initiate a network request
                // Perform the network request.                NetworkResponse networkResponse = mNetwork.performRequest(request);                request.addMarker(network-http-complete);
You can obtain a request result by executing the network's receivmrequest () method. After the execution, we get the request result. The default implementation is as follows:
    @Override    public NetworkResponse performRequest(Request
  request) throws VolleyError {        long requestStart = SystemClock.elapsedRealtime();        while (true) {            HttpResponse httpResponse = null;            byte[] responseContents = null;            Map
 
   responseHeaders = new HashMap
  
   ();            try {                // Gather headers.                Map
   
     headers = new HashMap
    
     ();                addCacheHeaders(headers, request.getCacheEntry());                httpResponse = mHttpStack.performRequest(request, headers);                StatusLine statusLine = httpResponse.getStatusLine();                int statusCode = statusLine.getStatusCode();                responseHeaders = convertHeaders(httpResponse.getAllHeaders());                // Handle cache validation.                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,                            request.getCacheEntry().data, responseHeaders, true);                }                // Some responses such as 204s do not have content.  We must check.                if (httpResponse.getEntity() != null) {                  responseContents = entityToBytes(httpResponse.getEntity());                } else {                  // Add 0 byte response as a way of honestly representing a                  // no-content request.                  responseContents = new byte[0];                }                // if the request is slow, log it.                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;                logSlowRequests(requestLifetime, request, responseContents, statusLine);                if (statusCode < 200 || statusCode > 299) {                    throw new IOException();                }                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);            } catch (SocketTimeoutException e) {                attemptRetryOnException(socket, request, new TimeoutError());            } catch (ConnectTimeoutException e) {                attemptRetryOnException(connection, request, new TimeoutError());            } catch (MalformedURLException e) {                throw new RuntimeException(Bad URL  + request.getUrl(), e);            } catch (IOException e) {                int statusCode = 0;                NetworkResponse networkResponse = null;                if (httpResponse != null) {                    statusCode = httpResponse.getStatusLine().getStatusCode();                } else {                    throw new NoConnectionError(e);                }                VolleyLog.e(Unexpected response code %d for %s, statusCode, request.getUrl());                if (responseContents != null) {                    networkResponse = new NetworkResponse(statusCode, responseContents,                            responseHeaders, false);                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||                            statusCode == HttpStatus.SC_FORBIDDEN) {                        attemptRetryOnException(auth,                                request, new AuthFailureError(networkResponse));                    } else {                        // TODO: Only throw ServerError for 5xx status codes.                        throw new ServerError(networkResponse);                    }                } else {                    throw new NetworkError(networkResponse);                }            }        }    }
    
   
  
 
Key part: httpResponse = mHttpStack. Route mrequest (request, headers); The network request is executed through this method. The specific details can still be customized. If there are too many code posts, you can check the two implementations. For example, the timeout time of network requests is set through request. getTimeoutMs. In the Request, you can know that the timeout setting is implemented through the following code:
/**     * Sets the retry policy for this request.     */    public void setRetryPolicy(RetryPolicy retryPolicy) {        mRetryPolicy = retryPolicy;    }

3.3 parse request results
// Parse the response here on the worker thread.                Response
  response = request.parseNetworkResponse(networkResponse);                request.addMarker(network-parse-complete);
This is not very familiar, we implement parseNetworkResponse () in the request ().
3.4 processing the final result. You can continue to look at the default code implementation. It is worth mentioning that here we will find that the mRequest. isCanceled () is called again to determine whether to pass the result to the user.
4. To sum up Volley. java, the purpose of this class is to get a RequestQueue object. From the code, we can see that to achieve this goal, we must create at least three key objects, Cache, Network, and HttpStack. Cache is a Cache for processing responses; Network is the interface for executing Network requests; HttpStack is the interface for actually executing Network requests. Both Network and HttpStack are interfaces, so volley reserves enough custom space for us. If it is completely customized, we can create our own HttpStack and Network implementation, so that we can no longer use the volley. java class.
This article has been written for half a month and is complete with recent emotional changes. Although the subsequent writing is getting worse and worse, the writing is finished now. Thank you for your advice ~~

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.