Android okhttp usage and analysis

Source: Internet
Author: User
Tags addall

In the field of Android development, many important issues have a good open source solution, such as network request OkHttp + Retrofit is simply the choice. "We don't have to reinvent the wheel to say that we don't need to know how to build a wheel and how to make it better!" "After nearly two years of using these good wheels, it's time to take the wheel apart. Based on the latest source code of OkHttp up to 2016.7.11, this paper analyzes it in detail.

1, the whole idea

Starting from the use of the method, the first is how to use, followed by the function we use in the internal how to achieve, the implementation of the scheme has what skills, what paradigm. The full text is basically a OkHttp source of an analysis and guidance, very recommended that you download OkHttp source code, followed by this article, over the source code. For the skills and paradigm, because at present my skill is not in place, the analysis content is not much, welcome everybody and I discuss together.

First put a complete flow chart (do not understand the matter, slowly looking back):

2, basic use Cases

From the official website of OkHttp.

2.1, create the Okhttpclient object [code]java code:
Okhttpclient client = new Okhttpclient ();

Hey, how come you don't see builder? Don't worry, and look at the constructor:

[Code]java Code:
Public Okhttpclient () {This  (new Builder ());}

The original is convenient for us to use, providing a "quick action", all using the default configuration. There OkHttpClient.Builder are a lot of class members, and we'll analyze it later, here we go briefly:

[Code]java Code:
Public Builder () {  dispatcher = new Dispatcher ();  protocols = Default_protocols;  Connectionspecs = Default_connection_specs;  Proxyselector = Proxyselector.getdefault ();  Cookiejar = cookiejar.no_cookies;  Socketfactory = Socketfactory.getdefault ();  Hostnameverifier = okhostnameverifier.instance;  Certificatepinner = Certificatepinner.default;  Proxyauthenticator = Authenticator.none;  Authenticator = Authenticator.none;  ConnectionPool = new ConnectionPool ();  DNS = Dns.system;  Followsslredirects = true;  Followredirects = true;  Retryonconnectionfailure = true;  ConnectTimeout = 10_000;  ReadTimeout = 10_000;  WriteTimeout = 10_000;}

2.2, initiate the HTTP request [Code]java code:
String run (string url) throws IOException {  Request request = new Request.builder ()      . URL (URL)      . Build ();  Response Response = client.newcall (Request). Execute ();  Return Response.body (). String ();}

OkHttpClientImplemented Call.Factory , responsible for creating new on request Call , in Demolition Wheel series: Demolition Retrofit We had a brief encounter with it:

callFactoryResponsible for creating the HTTP request, the HTTP request is abstracted to the okhttp3.Call class, which represents an HTTP request that is ready to be executed at any time

So let's see how it's created call now:

[Code]java Code:
/**  * Prepares the {@code request} to is executed at some point in the future.  */@Override Public call Newcall (Request request) {  return new Realcall (this, request);}

So it seems that the credit is all in RealCall class, below we analyze the process of synchronizing the network request, while understanding RealCall the specific content.

2.2.1, synchronizing network requests

First we look at RealCall#execute :

[Code]java Code:
@Override public Response Execute () throws IOException {  synchronized (this) {    if (executed) throw new Illegalstat Eexception ("Already Executed");  (1)    executed = true;  }  try {    client.dispatcher (). executed (this);                                 (2)    Response result = Getresponsewithinterceptorchain ();                (3)    if (result = = null) throw new IOException ("Canceled");    return result;  } finally {    client.dispatcher (). finished (this);                                 (4)  }}

Here are 4 things we do:

    1. Check that the call has been executed, that each call can only be executed once, and if you want an exact call, you can clone it using the call#clone method.
    2. Using it for client.dispatcher().executed(this) actual execution, dispatcher is one of the OkHttpClient.Builder members just seen, and its documentation says that it is an execution strategy for an asynchronous HTTP request, and now it seems that the synchronization request is also mixed.
    3. The call getResponseWithInterceptorChain() function gets the HTTP return result, as you can see from the function name, this step also carries out a series of "intercept" operations.
    4. Finally, you have to inform yourself that the dispatcher execution is complete.

Dispatcher here we do not over-focus, in the process of synchronous execution, the content involved in dispatcher is simply to inform it of our execution state, such as the start of execution (call executed ), such as execution completed (call finished ), It will have more involvement in the asynchronous execution process.

To actually make a network request, parse the return result, or getResponseWithInterceptorChain :

[Code]java Code:
Private Response Getresponsewithinterceptorchain () throws IOException {  //Build a full stack of interceptors.  List interceptors = new arraylist<> ();  Interceptors.addall (Client.interceptors ());  Interceptors.add (retryandfollowupinterceptor);  Interceptors.add (New Bridgeinterceptor (Client.cookiejar ()));  Interceptors.add (New Cacheinterceptor (Client.internalcache ()));  Interceptors.add (new Connectinterceptor (client));  if (!retryandfollowupinterceptor.isforwebsocket ()) {    Interceptors.addall (client.networkinterceptors ());  }  Interceptors.add (New Callserverinterceptor (      Retryandfollowupinterceptor.isforwebsocket ()));  Interceptor.chain Chain = new Realinterceptorchain (      interceptors, NULL, NULL, NULL, 0, originalrequest);  Return Chain.proceed (originalrequest);}

In one of the OkHttp developers who introduced OkHttp's article, the author said:

The whole thing is just a stack of built-in interceptors.

Visible Interceptor is OkHttp the core of a thing, do not mistakenly think it is only responsible for intercepting requests for some additional processing (such as cookies), in fact, it is the actual network requests, caching, transparent compression and other functions are unified, each function is only one Interceptor , they are connected to a Interceptor.Chain, interlocking, and finally successfully completed a network request.

From getResponseWithInterceptorChain the function we can see that Interceptor.Chain the distribution is sequentially:

    1. Set at the OkHttpClient time of configuration interceptors ;
    2. Responsible for failure retry and redirection RetryAndFollowUpInterceptor ;
    3. Responsible for translating user-constructed requests into requests sent to the server and translating the responses returned by the server into user-friendly responses BridgeInterceptor ;
    4. Responsible for reading the cache directly back, update the cache CacheInterceptor ;
    5. Responsible for establishing the connection with the server ConnectInterceptor ;
    6. OkHttpClientset when configured networkInterceptors ;
    7. Responsible for sending the request data to the server, reading the response data from the server CallServerInterceptor .

Here, the position determines the function, the last interceptor must be responsible for the actual communication with the server, redirection, caching, etc. must be before the actual communication.

The chain of responsibility has been Interceptor well practiced in this chain (thanks to stay gave away, inferiority).

It contains command objects and a series of processing objects, each processing object determines which command objects it can handle, and it knows how to pass a Command object that it cannot handle to the next processing object in the chain. The pattern also describes how to add a new processing object to the end of the processing chain.

For this to happen, Request Response each of Interceptor us is likely to accomplish this, so we follow the chain to make each Interceptor decision whether to complete the task and how to accomplish the task (self-reliance or the next one Interceptor ). In this way, the completion of the network request is completely stripped out of the RealCall class, simplifying the respective responsibilities and logic. Two words: Elegant!

The responsibility chain model also has the typical practice in the Android system, for example the view system to the Click event (touchevent) processing, specifically may refer to the Android design pattern source code Analysis responsibility chain pattern related analysis.

Back to OkHttp, here we first briefly analyze ConnectInterceptor and CallServerInterceptor , see how OkHttp is the actual communication with the server.

2.2.1.1, establish the connection: ConnectInterceptor[Code]java Code:
@Override public Response Intercept (Chain Chain) throws IOException {  Realinterceptorchain Realchain = ( Realinterceptorchain) chain;  Request Request = Realchain.request ();  Streamallocation streamallocation = Realchain.streamallocation ();  We need the network to satisfy this request. Possibly for validating a conditional GET.  Boolean doextensivehealthchecks =!request.method (). Equals ("GET");  Httpcodec Httpcodec = streamallocation.newstream (client, doextensivehealthchecks);  Realconnection connection = Streamallocation.connection ();  return Realchain.proceed (Request, Streamallocation, httpcodec, connection);}

Actually establishing a connection is creating an HttpCodec object that will be used in a later step, and where is it sacred? It is an abstraction of the HTTP protocol operation, with two implementations: Http1Codec and Http2Codec , as the name implies, they correspond to the implementations of the http/1.1 and HTTP/2 versions, respectively.

In Http1Codec , it encapsulates the read and Socket write operations of Okio, and Okio has the opportunity to analyze them later, and now let's keep a simple understanding of them: it's the right java.io and the java.nio encapsulation that makes it easier and more efficient for IO operations.

While the HttpCodec process of creating objects involves StreamAllocation , RealConnection and the code is longer, it is not expanded here, and this process, in a nutshell, is to find an available RealConnection , reusable RealConnection input and output ( BufferedSource and) to BufferedSink create HttpCodec objects for subsequent steps to use.

2.2.1.2, sending and receiving data: CallServerInterceptor[Code]java Code:
@Override public Response Intercept (Chain Chain) throws IOException {Httpcodec Httpcodec = ((Realinterceptorchain) Chain  ). Httpstream ();  Streamallocation streamallocation = ((Realinterceptorchain) chain). Streamallocation ();  Request Request = Chain.request ();  Long Sentrequestmillis = System.currenttimemillis ();  Httpcodec.writerequestheaders (Request); if (Httpmethod.permitsrequestbody (Request.method ()) && request.body () = null) {Sink requestbodyout = Httpcode    C.createrequestbody (Request, Request.body (). ContentLength ());    Bufferedsink bufferedrequestbody = Okio.buffer (requestbodyout);    Request.body (). WriteTo (Bufferedrequestbody);  Bufferedrequestbody.close ();  } httpcodec.finishrequest (); Response Response = Httpcodec.readresponseheaders (). Request (Request). Handshake (Streamallocation.connection (). h Andshake ()). Sentrequestatmillis (Sentrequestmillis). Receivedresponseatmillis (System.currenttimemillis ()).  Build (); if (!forwebsocket | | respOnse.code ()! = 101) {response = Response.newbuilder (). Body (Httpcodec.openresponsebody (response)). Build  (); if ("Close". Equalsignorecase (Response.request (). Header ("Connection")) | |  "Close". Equalsignorecase (Response.header ("Connection"))) {streamallocation.nonewstreams (); }//Omit partial check code return response;}

We grab the trunk section:

    1. Send the request header to the server;
    2. If a request body is available, it is sent to the server;
    3. Read response header, first constructs an Response object;
    4. If there is response body, on the basis of 3, add body to construct a new Response object;

Here we can see that the core work is HttpCodec done by the object, but HttpCodec in fact the use of Okio, and Okio is actually used Socket , so there is no mystery, but a layer of layer, a bit more layers.

Interceptorin fact, the design is also a layered thinking, each Interceptor is a layer. Why do we have to cover so many layers? Layered thinking in the TCP/IP protocol is the embodiment of the most vividly, layered simplification of each layer of logic, each layer only need to pay attention to their own responsibility (the idea of a single principle is also reflected here), and each layer through the agreed interface/protocol for cooperation (interface-oriented programming ideas), the joint completion of complex tasks.

Simplicity should be one of our ultimate pursuits, although sometimes it has to be complicated to achieve goals, but if there is another simpler way, I think no one should be reluctant to replace them.

2.2.2, initiating an asynchronous network request [code]java code:
client.newcall (Request) Enqueue (new Callback () {@Override public void OnF Ailure (call call, IOException e) {} @Override public void Onresponse (call call, Response Response) throws Ioexce    ption {System.out.println (Response.body (). String ()); }});//Realcall#enqueue@override public void Enqueue (Callback responsecallback) {synchronized (this) {if (executed)    throw new IllegalStateException ("Already Executed");  executed = true; } client.dispatcher (). Enqueue (New Asynccall (Responsecallback));} dispatcher#enqueuesynchronized void Enqueue (Asynccall call) {if (Runningasynccalls.size () < maxrequests && ;    Runningcallsforhost (call) < Maxrequestsperhost) {Runningasynccalls.add (call);  Executorservice (). Execute (call);  } else {readyasynccalls.add (call); }}

Here we can see the role of dispatcher in the asynchronous execution, if the current can also execute a concurrent request, then execute immediately, otherwise join the readyAsyncCalls queue, and the execution of the request after execution, will call the promoteCalls() function, to the readyAsyncCalls queue AsyncCall "promotion "As runningAsyncCalls , and begin to execute.

Here AsyncCall is RealCall an inner class, which is implemented so that it Runnable can be committed to ExecutorService execution, and it executes when it invokes the getResponseWithInterceptorChain() function and passes the result to the responseCallback upper user.

In this sense, the principle of synchronous requests and asynchronous requests is the same as the getResponseWithInterceptorChain() Interceptor network request logic that is implemented through the chain in the function, and asynchronous is ExecutorService achieved by the implementation.

2.3, returns the data obtained

After the above synchronization ( Call#execute() after execution) or asynchronous ( Callback#onResponse() callback) request is complete, we can get the Response response data from the object, including the HTTP status Code,status message,response header, Response body and so on. The body part is the most special, because the data returned by the server can be very large, so it has to be accessed by means of a data stream (which, of course, provides a string() bytes() way to read the data within the stream once), while the rest of the response is freely available.

The response body is encapsulated into the ResponseBody class, which has two main points to note:

    1. Each body can only be consumed once, multiple consumption will throw an exception;
    2. The body must be closed, otherwise a resource leak will occur;

In 2.2.1.2, send and receive data: In the Callserverinterceptor section, we have seen the body-related code:

[Code]java Code:
if (!forwebsocket | | Response.code ()! = 101) {  response = Response.newbuilder ()      . Body ( Httpcodec.openresponsebody (response))      . Build ();}

The HttpCodec#openResponseBody response body, which provides a specific version of the HTTP protocol, HttpCodec is implemented using Okio for specific data IO operations.

Here is a point worth mentioning, OkHttp on the response is very strict, HTTP status line can not have any messy data, otherwise it will throw an exception, in our company project practice, due to server problems, occasionally status line will have additional data, And the problem of the service side has no clue, cause we have to reluctantly continue to use HttpURLConnection, and the latter in some systems have a variety of other problems, such as Meizu system sent multi-part form when there is no response to the problem.

2.4,http Cache

In 2.2.1, the Synchronization network Request section, we have seen Interceptor the layout, before establishing the connection, and the server communication, is CacheInterceptor , before establishing a connection, we check whether the response has been cached, the cache is available, if it is directly return the cached data, otherwise the subsequent process, And before returning, writes the network data to the cache.

This code is more, but also very intuitive, mainly related to the implementation of the HTTP protocol cache details, and the specific cache logic OkHttp built-in encapsulation of a Cache class, it uses DiskLruCache , with a limited amount of space on disk cache, according to the LRU algorithm for cache elimination, here is no longer expanded.

We can set the OkHttpClient object at construction time Cache , and in its constructor we can specify the directory and cache size:

[Code]java Code:
Public Cache (File directory, long MaxSize);

And if we are not satisfied with the OkHttp built-in Cache classes, we can implement our own InternalCache interfaces and OkHttpClient set them at construction time so that we can use our custom caching strategy.

3, Summary

OkHttp There are many details that are not in this article, such as HTTP2/HTTPS support, but it is important to build a clear overview. With a clear understanding of the whole, the details will be easier to drill down on if necessary.

At the end of the article, let's review the complete flowchart:

    • OkHttpClientImplementation Call.Factory , responsible for Request creating Call ;
    • RealCallFor the concrete Call implementation, its enqueue() asynchronous interface is implemented by Dispatcher using the ExecutorService implementation, and the synchronization interface is consistent with the final network request execute() getResponseWithInterceptorChain() ;
    • getResponseWithInterceptorChain()In the use of Interceptor chain, layered to achieve cache, transparent compression, network IO and other functions;

Android okhttp usage and analysis

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.