In the past, Xutils was used on Android because it could process the two aspects of pictures and networks. Later, I found out that Xutils used HttpClient. Google had abolished HttpClient on the 6.0 version, so I started looking for a new network framework. OKHttp has also been used, but it is used on UI threads. It also needs to use handlers to use, so I used the Volley framework first. I'll analyze it here firstSimple network requests for Volley frameworksource code.
Simple process of requesting network data using Volley:
RequestQueue queue = (this); //Instantiate a request queue. Google recommends writing a singleton class to get the only queue StringRequest request = new StringRequest(, url1, new <String>() { @Override public void onResponse(String response) { (, "success"+response, Toast.LENGTH_SHORT).show(); } }, new () { @Override public void onErrorResponse(VolleyError error) { (, "Failed"+(), Toast.LENGTH_SHORT).show(); } }){ @Override protected Map<String, String> getParams() throws AuthFailureError { //Rewrite this function and submit parameters. You can also rewrite a Request to implement this method. Map<String,String> params = new HashMap<>(); (aaa+"name","1233555"); //parameter return params; } }; (request);
The request processing started to be executed when newRequestQueue. However, there was no request in the request queue at that time, so it blocked. When the add method was executed, it started to actually request the network.
So let's first look at the (request) method
public <T> Request<T> add(Request<T> request) { // Tag the request as belonging to this queue and add it to the set of current requests. (this); synchronized (mCurrentRequests) { (request); //Add to the current queue } // Process requests in the order they are added. (getSequenceNumber()); ("add-to-queue"); //Set the flag // If the request is uncacheable, skip the cache queue and go straight to the network. if (!()) { //Cache according to whether you need to cache. If you don't need to cache, just join the network task queue and then return. If you need to cache, then add the cache queue in the following code. By default, cache is required. (request); return request; } // Insert request into stage if there's already a request with the same cache key in flight. synchronized (mWaitingRequests) { String cacheKey = (); if ((cacheKey)) { //Judge whether the request that is currently being processed and can be cached contains the key of the request. If the inclusion indicates that there is already an identical request, then add it to it. // There is already a request in flight. Queue up. Queue<Request<?>> stagedRequests = (cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList<Request<?>>(); } (request); (cacheKey, stagedRequests); if () { ("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { //If not included, add an empty request to the temporary queue and then add it to the cache queue // Insert 'null' queue for this cacheKey, indicating there is now a request in // flight. (cacheKey, null); (request); } return request; } }
Analyze add methodFirst add to the mCurrentRequests collection. This set stores all requests processed by this queue. Then determine whether this request needs to be cached. If there is no cache, then directly join the mNetworkQueue queue and wait for processing. If necessary, then join to the mCacheQueue queue in the end, because RequestQueue will always process the cached tasks first when processing the request. If there is no cache during the first processing, it will still be added to the mNetworkQueue queue for processing. If there is a cache, then directly obtain the cache. Then determine whether there is the same request in the current request. If there is no cache, then add this request to the temporary collection. If there is not, add an empty request to the temporary queue to determine whether there is the same request as this request, and then add it to the cache queue.
Then let's look at the creation process of RequestQueue
public static RequestQueue newRequestQueue(Context context, HttpStack stack) { File cacheDir = new File((), DEFAULT_CACHE_DIR); //Create a file for cache String userAgent = "volley/0"; //User Agent Initialization try { String packageName = (); PackageInfo info = ().getPackageInfo(packageName, 0); userAgent = packageName + "/" + ; //The user agent is the app package name + version number } catch (NameNotFoundException e) { } if (stack == null) { //If HttpStack is not passed in, then the following default is adopted. Here you can rewrite the extension of HttpStack by yourself, which reflects the high scalability of the framework. if (.SDK_INT >= 9) { //If the sdk version is higher than 2.3, HurlStack is implemented internally. stack = new HurlStack(); } else { //If the version is lower than 2.3, use httpClientStack // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: /2011/09/ stack = new HttpClientStack((userAgent)); } } Network network = new BasicNetwork(stack); //Create a network to work only on requesting the network RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); //Instantiate a request queue Pass in parameters (); return queue; }
</pre><pre code_snippet_ snippet_file_name="blog_20160512_5_2241745" name="code" class="java">public RequestQueue(Cache cache, Network network, int threadPoolSize) { //Constructor will create the default ExecutorDelivery for callback this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(()))); }
The creation process of RequestQueue is also relatively simple. �
Then we check the queue's start method
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); //Create a cache scheduler and executes the run method after starting a thread. (); // Create network dispatchers (and corresponding threads) up to the pool size. for (int i = 0; i < ; i++) { //There will be 4 NetworkDispatchers by default to improve efficiency and execute the request in netWorkQueue NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; (); } }
This method first executes the cache scheduler thread and then executes 4 network work scheduler threads, because the cache scheduler will determine whether it has been cached. If it has been cached and has not expired, the cache will be reused directly and the task will not be added to the netWordQueue. Therefore, the following NetWork scheduler thread will not be able to retrieve the request and block and will not be executed. If there is no cache, the cache scheduler thread will add the request to the NetWork queue, and the following netWork scheduler will retrieve the request and execute it.
Let's take a closer look at the source code of the CacheDispatcher thread:
The code of the run method is relatively long. Let's take a look at it separately.Part One:
@Override public void run() { if (DEBUG) ("start new dispatcher"); (Process.THREAD_PRIORITY_BACKGROUND); //Set the priority of the thread to 10 // Make a blocking call to initialize the cache. (); //Initialize the cache while (true) { try { // Get a request from the cache triage queue, blocking until // at least one is available. final Request<?> request = (); //Fetch a request from the cache queue If not, it will block ("cache-queue-take"); //Add a tag // If the request has been canceled, don't bother dispatching it. if (()) { ("cache-discard-canceled"); continue; } // Attempt to retrieve this item from cache. entry = (()); //Read cache from cache if (entry == null) { //If the cache is not read ("cache-miss"); //Add cache miss tag // Cache miss; send off to the network dispatcher. (request); //Replace cache failed Add to netWork waiting for request continue; } // If it is completely expired, just send it to the network. if (()) { //Judge whether the cache has expired. If it has expired, then add it to netWork to wait for the request ("cache-hit-expired"); (entry); (request); continue; }
Part 2:
// We have a cache hit; parse its data for delivery back to the request. ("cache-hit"); //After executing this, it means that the cache has not expired and can be used Response<?> response = ( //Parse the read cache content into a Response object new NetworkResponse(, )); ("cache-hit-parsed"); //Add a tag if (!()) { //If the cache does not need to be refreshed, call the method directly. The listener interface that requests will be called back. // Completely unexpired cache hit. Just deliver the response. (request, response); } else { //If refresh is required, add the request to mNetworkQueue and wait for the request // Soft-expired cache hit. We can deliver the cached response, // but we need to also send the request to the network for // refreshing. ("cache-hit-refresh-needed"); (entry); // Mark the response as intermediate. = true; // Post the intermediate response back to the user and have // the delivery then forward the request along to the network. (request, response, new Runnable() { @Override public void run() { try { (request); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } } }
The specific process of the above code is also very simple. First, take out a request from the cache request queue, and check whether there is a cache for the request in the cache. If not, then put the request into the NetWork scheduler and wait for the call. If there is, there are several situations. If the obtained is empty, put it in NetWork. If it expires, put it in NetWork. If it does not need to refresh, directly get the response information from the cache and parse it. Then use the mDelivery callback interface. If it needs to refresh, put it in the NetWOrd queue and wait for the call. . .
Let's take a look at the code of the NetworkDispatcher thread. Similar to the code of CacheDispatcher:
@Override public void run() { (Process.THREAD_PRIORITY_BACKGROUND); //Set priority 10 while (true) { long startTimeMs = (); //Get the request execution start time Request<?> request; try { // Take a request from the queue. request = (); // Get a request from the queue. If there is no, blocking } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { ("network-queue-take"); // If the request was cancelled already, do not perform the // network request. if (()) { ("network-discard-cancelled"); continue; } addTrafficStatsTag(request); // Perform the network request. NetworkResponse networkResponse = (request); //Really execute the requested function and return the response ("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 ( && ()) { ("not-modified"); continue; } // Parse the response here on the worker thread. Response<?> response = (networkResponse); //Parse the response ("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s. if (() && != null) { //If cache is required, then store the response information in the cache ((), ); ("network-cache-written"); } // Post the response back. (); (request, response); //Can some methods afterward callback } catch (VolleyError volleyError) { (() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); //Callback error interface } catch (Exception e) { (e, "Unhandled exception %s", ()); VolleyError volleyError = new VolleyError(e); (() - startTimeMs); (request, volleyError); //Callback error interface } } }
NetworkDispatcher thread execution process. First get a request from networkDispatch and then determine whether it is cancelled. If not, then execute NetWOrk's performRequest method. Execute http request. The real request data is inside this function. After the request, determine whether it is put into the cache according to the set shouldCache flag. Then call back some interface methods. This way a request is completed.
Finally, let’s take a look at how the NetWork class (request) method submits the request. The code is relatively long but not difficult:
@Override public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = (); //Record the start time while (true) { HttpResponse httpResponse = null; byte[] responseContents = null; Map<String, String> responseHeaders = (); //Initialization response header is empty try { // Gather headers. Map<String, String> headers = new HashMap<String, String>(); //Request header addCacheHeaders(headers, ()); //Add request header according to cache httpResponse = (request, headers); //Calling the HttpStack method to request the network StatusLine statusLine = (); int statusCode = (); responseHeaders = convertHeaders(()); //Get the response header // Handle cache validation. if (statusCode == HttpStatus.SC_NOT_MODIFIED) { //If the cache is read for 304 Entry entry = (); // Check if it has been cached before if (entry == null) { //If the cached previously was empty, then it means that the request cached last time was also empty. Return the response directly return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, responseHeaders, true, () - requestStart); } // A HTTP 304 response does not have all header fields. We // have to use the header fields from the cache entry plus // the new ones from the response. // http:///Protocols/rfc2616/#sec10.3.5 (responseHeaders); //If it is not empty, then add the header and return the data return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, , , true, () - requestStart); } // Some responses such as 204s do not have content. We must check. if (() != null) { //Not a 304 situation
responseContents = entityToBytes(()); //Get the content of the response and return the response below } 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 = () - requestStart; logSlowRequests(requestLifetime, request, responseContents, statusLine); if (statusCode < 200 || statusCode > 299) { throw new IOException(); } return new NetworkResponse(statusCode, responseContents, responseHeaders, false, () - requestStart); } catch (SocketTimeoutException e) { attemptRetryOnException("socket", request, new TimeoutError()); } catch (ConnectTimeoutException e) { attemptRetryOnException("connection", request, new TimeoutError()); } catch (MalformedURLException e) { throw new RuntimeException("Bad URL " + (), e); } catch (IOException e) { int statusCode = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { statusCode = ().getStatusCode(); } else { throw new NoConnectionError(e); } ("Unexpected response code %d for %s", statusCode, ()); if (responseContents != null) { networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false, () - requestStart); 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); } } }
Then look at the request code of HttpStack:
@Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { String url = (); HashMap<String, String> map = new HashMap<String, String>(); (()); //Add a request header (additionalHeaders); if (mUrlRewriter != null) { String rewritten = (url); if (rewritten == null) { throw new IOException("URL blocked by rewriter: " + url); } url = rewritten; } URL parsedUrl = new URL(url); HttpURLConnection connection = openConnection(parsedUrl, request); //Open the connection for (String headerName : ()) { //Set the header (headerName, (headerName)); } setConnectionParametersForRequest(connection, request); //Add requested parameters and some basic information configuration in this function // Initialize HttpResponse with data from the HttpURLConnection. ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); int responseCode = (); //The following are some processing after obtaining the response information if (responseCode == -1) { // -1 is returned by getResponseCode() if the response code could not be retrieved. // Signal to the caller that something was wrong with the connection. throw new IOException("Could not retrieve response code from HttpUrlConnection."); } StatusLine responseStatus = new BasicStatusLine(protocolVersion, (), ()); BasicHttpResponse response = new BasicHttpResponse(responseStatus); (entityFromConnection(connection)); for (Entry<String, List<String>> header : ().entrySet()) { if (() != null) { Header h = new BasicHeader((), ().get(0)); (h); } } return response; }
This function is mainly used to use HttpUrlConnection. Add header in the method. Adding parameters requires getting the stream and then writing the parameters. The following function is introduced. Assume that it is the post method:
case : ("POST"); addBodyIfExists(connection, request); break;
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { byte[] body = (); if (body != null) { (true); (HEADER_CONTENT_TYPE, ()); DataOutputStream out = new DataOutputStream(()); (body); (); } }
Just write the body to the stream. The parameters are encapsulated in the body.
public byte[] getBody() throws AuthFailureError { Map<String, String> params = getParams(); if (params != null && () > 0) { return encodeParameters(params, getParamsEncoding()); } return null; } getParamsmethod yesRequest需要重写的一个method 返回值就yes参数的Mapgather [java] view plain copy existCODEView code fragments derived from my code fragments private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) { StringBuilder encodedParams = new StringBuilder(); try { for (<String, String> entry : ()) { (((), paramsEncoding)); ('='); (((), paramsEncoding)); ('&'); } return ().getBytes(paramsEncoding); } catch (UnsupportedEncodingException uee) { throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee); } }
This function is to splice string parameters according to certain rules. Then you can submit the parameters.
Finally, let’s introduce the main categories, members and their functions of this framework:
RequestQueue The queue used to process the requests, all the requests are placed in this class. Call the start method to start processing the request.
mCache The requested cache will be put into this cache when a request is submitted and this request needs to be cached.
mNetwork The interface is simply used to submit network requests. There is only one method to submit requests. An HttpStack is required to complete the submission of the request.
mDelivery Used for interface callbacks and other functions after request response
mDispatchersNetWork scheduler thread array contains 4 object processing requests. The purpose is to improve efficiency. When there is no cache to obtain or has expired, the run method of this thread will be called. If not, blocking.
mCacheDispatcher Cache scheduler threads Processing cached requests If there is no cache, put the request in NetWorkQueue and wait for the call
The above is all about this article, and I hope it will be helpful for everyone to learn the Android Volley framework.