Okhttp complete parsing of the overall invocation process

Source: Internet
Author: User
Tags addall

Reading good code is as wonderful as watching the beautiful scenery

Okhttp is an open source Java Network Request framework for the square company in GitHub, which is very popular. OkHttp code is not particularly large, the code is a lot of clever implementation, it is worth learning.
It is recommended to download a okhttp source code, use idea or source Insight to read, follow this article to read.

Global Overview of Okhttp:

Analysis of a complex framework that can first
Analysis of simple invocation processes
Then analyze the relationships between the global classes

Class diagram of OkHttp

From: http://frodoking.github.io/2015/03/12/android-okhttp/

How to write a complex framework, Okhttp gives us a good demonstration, which conforms to the design pattern of the façade pattern , the core of the framework is okhttpclient.
All of the user's operations are implemented by the Operation Okhttpclient, okhttpclient below a lot of sub-modules, okhttpclient know all the implementation of the Submodule, Okhttpclient call each sub-module completion function, the sub-modules are independent,
They do not know the existence of okhttpclient.

Okhttp flowchart

(from Piasy great God)

                 

Although the okhttpclient is complex, the logic is still very concise.
Can be divided into three stages

    • 1.OKHttpClient +request Construction Realcall
    • 2.RealCall direct synchronous execution or into an asynchronous queue, distributed uniformly by dispatcher
    • 3. Through the interceptor chain of responsibility, layer calls, and finally get Response
The simplest usage of okhttp is:
OkHttpClient client = new OkHttpClient();String run(String url) throws IOException {  Request request = new Request.Builder()      .url(url)      .build();  Response response = client.newCall(request).execute();  return response.body().string();}

Here, we are using the simplest function, using only three okhttpclient,request, Response.
It can be said that okhttp from the design of the time to consider the ease of
A network request framework, the simplest only needs
With Okhttpclient,request,response, you can initiate a request and a response.

The following is an analysis of the entire initiating request, receiving the response process:

1.0 Creating Okhttpclient

This is the factory that initiated all call calls, including sending HTTP Request, and receiving Response
Due to the okhttpclient internal implementation of the connection pool, the thread pool, etc., so
Each application typically requires only a single singleton okhttpclient, which can be reused for HTTP requests without worrying about the scalability of the singleton, which is very easy to use and extensible (because of the okhttp's flexible builder mode: O Khttpclent.builder):

For example:

If you only need the default configuration of Okhttpclient:
So
All you need to do is:
Okhttpclient client=new okhttpclient ();
Why is there no builder? Because the constructor okhttpclient () already uses the default new Buidler () by default;
In fact

public OkHttpClient() {    this(new Builder());  }  private OkHttpClient(Builder builder) {   this.dispatcher = builder.dispatcher;  //省略代码  }

If you do not use the default configuration, then we cannot use Okhttpclient (Builder builder)
This private method new is a okhttpclient instance of the
Can do this:
Okhttpclient.builder bulider=new Okhttpclient.builder ();
Okhttpclient client=buider.xxx (). XXX (). build ();

So will the okhttpclient of the single case affect the extensibility of okhttpclient?
No, the reason why we want to singleton one okhttpclient is that the connection pool and the thread pool should not be created repeatedly

In other words, a okhttpclient can be defined globally, and when a okhttpclient is required for a special configuration, the local okhttpclient
The referenced connection pool and thread pool are multiplexed from the global Singleton;
Such as:

  OkHttpClient eagerClient = client.newBuilder()        .readTimeout(500, TimeUnit.MILLISECONDS)        .build();    Response response = eagerClient.newCall(request).execute();

The implementation is: through the global Okhttpclient Singleton new property of the same builder, and then to the builder separately configure local properties
, you can create a local okhttpclient by using this builder.

 public Builder newBuilder() {    return new Builder(this);  }Builder(OkHttpClient okHttpClient) {      this.dispatcher = okHttpClient.dispatcher;      this.proxy = okHttpClient.proxy;       //省略代码  }
1.1 Creating a request
Request request = new Request.Builder()      .url(url)      .build();

The same builder mode is used

Note: Request contains information that is only based on URLs, body, header,tag,method, etc.

1.2 Create a Realcall with Okhttpclient via new call request
OkHttpClient: @Override public Call newCall(Request request) {    return new RealCall(this, request, false /* for web socket */);  }
1.3 Creating Realcall

Okhttp network requests are divided into both synchronous and asynchronous situations:
Synchronization
Call directly
Realcall's Execute method, undergoes a series of interceptors Intercepter, returns response
Asynchronous
By Realcall the Enqueue method, create a asynccall, put this asynccall through the client's dispatcher
Distribution continues

1.4 Synchronous execution of requests and asynchronous execution of the distribution process in dispatcher: 1 synchronous requests
    • Realcall.java
@Override public Response execute() throws IOException {    synchronized (this) {      if (executed) throw new IllegalStateException("Already Executed");      executed = true;    }    captureCallStackTrace();    eventListener.callStart(this);    try {      client.dispatcher().executed(this);//通过Dispatcher记录这个在       //最重要是这一句,通过拦截器的调用链获取Response,同步执行      Response result = getResponseWithInterceptorChain();      if (result == null) throw new IOException("Canceled");      return result;    } catch (IOException e) {      eventListener.callFailed(this, e);      throw e;    } finally {      client.dispatcher().finished(this);//结束后,Dispatcher负责清理这个记录    }  }
2 Asynchronous requests:

Constructs an object of Realcall inner class Asynccall by constructing Responsecallback,
and insert it into the dispatcher queue.

    • Realcall.java
@Override public void enqueue(Callback responseCallback) {    synchronized (this) {      if (executed) throw new IllegalStateException("Already Executed");      executed = true;    }    captureCallStackTrace();    eventListener.callStart(this);    client.dispatcher().enqueue(new AsyncCall(responseCallback));  }
    • Asynccall.java
      Because Asynccall is an inner class of realcall, it has an object that corresponds to an instance of the external class realcall, and it implements the Runnable interface, so it can be executed by the thread pool
  Final class Asynccall extends Namedrunnable {private final Callback responsecallback;      Asynccall (Callback responsecallback) {super ("OkHttp%s", Redactedurl ());    This.responsecallback = Responsecallback;    } String Host () {return Originalrequest.url (). host ();    } request request () {return originalrequest;    } Realcall get () {return realcall.this;      } @Override protected void Execute () {Boolean signalledcallback = false;        try {Response Response = Getresponsewithinterceptorchain ();          if (retryandfollowupinterceptor.iscanceled ()) {signalledcallback = true;        Responsecallback.onfailure (Realcall.this, New IOException ("Canceled"));          } else {signalledcallback = true;        Responsecallback.onresponse (realcall.this, response);          }} catch (IOException e) {if (signalledcallback) {//Do not signal the callback twice! Platform.get (). log (INFO, "Callback Failure for "+ tologgablestring (), E);          } else {eventlistener.callfailed (realcall.this, E);        Responsecallback.onfailure (Realcall.this, E);      }} finally {Client.dispatcher (). finished (this); }    }  }
    • Dispatcher.java
  Note This is a synchronous method synchronized void Enqueue (Asynccall call) {if (Runningasynccalls.size () < maxrequests && Ru Nningcallsforhost (call) < Maxrequestsperhost) {//In order to avoid too many requests to execute simultaneously, there are two limits,//the maximum number of simultaneous requests cannot exceed maxrequests, and for each      Hosts host (server), and the number of requests cannot exceed Maxrequestsperhost runningasynccalls.add (call);    Executorservice (). Execute (call);    } else {//otherwise join a ready readyasynccalls.add (call); The}//synchronization method gets a single instance of the thread pool public synchronized Executorservice Executorservice () {if (Executorservice = = null) {//This line Pool parameter settings There's a lot of learning, too, Executorservice = new Threadpoolexecutor (0, Integer.max_value, timeunit.seconds, new Sync    Hronousqueue<runnable> (), Util.threadfactory ("OkHttp Dispatcher", false));  } return Executorservice; }//at the end of a call execution private <T> void finished (deque<t> calls, T call, Boolean promotecalls) {int runningcalls    Count;    Runnable Idlecallback; Synchronized (this) {if (!calls.remove (call)) throw newAssertionerror ("Call wasn ' t in-flight!");      A new call execution needs to be removed, and promotecalls () is called to automatically execute N calls until the number limit is reached if (promotecalls) promotecalls ();      Runningcallscount = Runningcallscount ();    Idlecallback = This.idlecallback;    } if (Runningcallscount = = 0 && Idlecallback! = null) {Idlecallback.run (); }}//Active fetch ready call execution private void Promotecalls () {if (Runningasynccalls.size () >= maxrequests) return;//Alrea    DY running max capacity. if (Readyasynccalls.isempty ()) return;    No ready calls to promote. for (Iterator<asynccall> i = Readyasynccalls.iterator (); I.hasnext ();)      {Asynccall call = I.next ();        if (Runningcallsforhost (call) < Maxrequestsperhost) {i.remove ();        Runningasynccalls.add (call);      Executorservice (). Execute (call); } if (Runningasynccalls.size () >= maxrequests) return;    reached Max capacity. }  }
1.5 Interceptor the principle and flow of chain-call interception

In Okhttp, for example we have a variety of needs, need to transparently compress gzip, our request requests only the content of our settings, need to add
The HTTP protocol header's header information is also allowed to allow the user to write their own logic in the process of inserting calls, where it is necessary to understand Okhttp's chained call interception logic
First, suppose the entire request response is a black box

We
The input is application request

Response of output application

The middle has experienced some interceptors, has okhttp own definition, can also have the custom:

Such as:

Response getResponseWithInterceptorChain() throws IOException {    // Build a full stack of interceptors.    List<Interceptor> 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 (!forWebSocket) {      interceptors.addAll(client.networkInterceptors());    }    interceptors.add(new CallServerInterceptor(forWebSocket));    Interceptor.Chain chain = new RealInterceptorChain(        interceptors, null, null, null, 0, originalRequest);    return chain.proceed(originalRequest);  }

As you can see from the source code, a List is created

Public Response Proceed (Request request, Streamallocation Streamallocation, Httpcodec httpcodec, Connection connectio    N) throws IOException {if (index >= interceptors.size ()) throw new Assertionerror ();    calls++;    If we already has a stream, confirm that the incoming request would use it. if (This.httpcodec! = null &&!sameconnection (Request.url ())) {throw new IllegalStateException ("Network inte    Rceptor "+ interceptors.get (index-1) +" must retain the same host and Port ");    }//If We already has a stream, confirm that's the only call to Chain.proceed (). if (This.httpcodec! = null && calls > 1) {throw new IllegalStateException ("Network Interceptor" + INTERC    Eptors.get (index-1) + "must call proceed () exactly once");    }//Call the next interceptor in the chain. Realinterceptorchain next = new Realinterceptorchain (interceptors, streamallocation, Httpcodec, connection, index + 1, requEST);    Interceptor Interceptor = Interceptors.get (index);    Response Response = interceptor.intercept (next);    Confirm that the next interceptor made it required call to Chain.proceed (). if (Httpcodec! = NULL && index + 1 < interceptors.size () && next.calls! = 1) {throw new Illegalst    Ateexception ("Network Interceptor" + Interceptor + "must call proceed () exactly once");    }//Confirm that the intercepted response isn ' t null.    if (response = = null) {throw new NullPointerException ("interceptor" + Interceptor + "returned null"); } return response;

Where: The most important code is as follows:
Realinterceptorchain next = new Realinterceptorchain (
Interceptors, streamallocation, Httpcodec, connection, index + 1, request);
Interceptor Interceptor = Interceptors.get (index);
Response Response = interceptor.intercept (next);

First create a chain of next
Then get the current interceptor based on index
Call Interceptor.intercept (next), pass in the next Nextchain, and execute the interception logic

Note that there are two cases, intercept

The interceptor can decide whether to return reponse directly to end all chained calls, or to continue calling Nextchain.proceed to perform the next interception call, returning response

The most important interceptor:callserverinterceptor

This interceptor is the last interceptor that is responsible for sending the request to the server

/** the last interceptor in the chain. It makes a network call to the server.  */public Final class Callserverinterceptor implements interceptor {private Final Boolean forwebsocket;  Public Callserverinterceptor (Boolean forwebsocket) {this.forwebsocket = Forwebsocket; } @Override public Response intercept (Chain Chain) throws IOException {Realinterceptorchain Realchain = (realintercep    Torchain) chain;    Httpcodec Httpcodec = Realchain.httpstream ();    Streamallocation streamallocation = Realchain.streamallocation ();    Realconnection connection = (realconnection) realchain.connection ();    Request Request = Realchain.request ();    Long Sentrequestmillis = System.currenttimemillis ();    Realchain.eventlistener (). Requestheadersstart (Realchain.call ());    Httpcodec.writerequestheaders (Request);    Realchain.eventlistener (). Requestheadersend (Realchain.call (), request);    Response.builder responsebuilder = null; if (Httpmethod.permitsrequestbody (Request.method ()) && Request.body ()! = NULL) {//If there ' s a "expect:100-continue" header on the request, wait for a "http/1.1 10 0//Continue "response before transmitting the request body.      If we don ' t get that, return//What we do get (such as a 4xx response) without ever transmitting the request body.        if ("100-continue". Equalsignorecase (Request.header ("Expect"))) {httpcodec.flushrequest ();        Realchain.eventlistener (). Responseheadersstart (Realchain.call ());      Responsebuilder = Httpcodec.readresponseheaders (true);        } if (Responsebuilder = = null) {//Write the request body if the "expect:100-continue" expectation was met.        Realchain.eventlistener (). Requestbodystart (Realchain.call ());        Long contentlength = Request.body (). ContentLength ();        Countingsink requestbodyout = new Countingsink (httpcodec.createrequestbody (Request, contentlength));      Bufferedsink bufferedrequestbody = Okio.buffer (requestbodyout);  Request.body (). WriteTo (Bufferedrequestbody);        Bufferedrequestbody.close ();      Realchain.eventlistener (). Requestbodyend (Realchain.call (), requestbodyout.successfulcount); } else if (!connection.ismultiplexed ()) {//If the ' expect:100-continue ' expectation wasn ' t met, prevent the HTTP /1 connection//from being reused.        Otherwise we ' re still obligated to transmit the request body to//leave the connection in a consistent state.      Streamallocation.nonewstreams ();    }} httpcodec.finishrequest ();      if (Responsebuilder = = null) {Realchain.eventlistener (). Responseheadersstart (Realchain.call ());    Responsebuilder = Httpcodec.readresponseheaders (false); } Response Response = responsebuilder. Request. Handshake (Streamallocation.connection (). Handshak E ()). Sentrequestatmillis (Sentrequestmillis). Receivedresponseatmillis (System.currenttimemillis ()). b    Uild (); Realchain.evEntlistener (). Responseheadersend (Realchain.call (), response);    int code = Response.code (); if (forwebsocket && code = = 101) {//Connection is upgrading, but we need to ensure interceptors see a non-n      ull response body.    Response = Response.newbuilder (). Body (Util.empty_response). build ();    } else {response = Response.newbuilder (). Body (Httpcodec.openresponsebody (response)). Build (); if ("Close". Equalsignorecase (Response.request (). Header ("Connection")) | |    "Close". Equalsignorecase (Response.header ("Connection"))) {streamallocation.nonewstreams (); } if (code = = 204 | | code = = 205) && response.body (). ContentLength () > 0) {throw new Protocolexceptio    N ("HTTP" + code + "had Non-zero content-length:" + response.body (). ContentLength ());  } return response;    } Static final class Countingsink extends Forwardingsink {long successfulcount; Countingsink (SinkDelegate) {super (delegate);      } @Override public void write (Buffer source, long ByteCount) throws IOException {super.write (source, byteCount);    Successfulcount + = ByteCount; }  }}
Summarize

Okhttp The overall call flow is still very clear, you can see the author's skill. When we analyze, we focus on the process of requesting a request from initiation to completion of a response that is easier to understand.

Okhttp complete parsing of the overall invocation process

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.