preface

The most popular web request frameworks in Android development right now are OkHTTP and Retrofit, both square products. Both are excellent open source libraries. It’s worth reading their source code and learning about their design philosophy. But retrofit actually uses OKHTTP to initiate web requests, so understanding okHTTP is also understanding RetroFIT, and their source code should be read in the order of okHTTP first. I found its most recent commit on Retrofit before updating okHTTP to 3.14. The latest version of OKHTTP is 4.0.x, and okHTTP was written in Kotlin since 4.0.x. Before that, it was written in Java. However, the source version of OKHTTP for this analysis is basically 3.14.x, which version is not important, what is important is the harvest after reading. I plan to analyze OKHTTP in two articles, which are:

  • Request flow (synchronous, asynchronous)
  • The Interceptor (Interceptor)

This article is the first article – okHTTP request flow, okHTTP project address: okhttp

Simple use of okHTTP

Let’s recall okHTTP using a simple GET request and use this example to illustrate the okHTTP request flow as follows:

// create OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
    .readTimeout(5, TimeUnit.SECONDS)
    .build();

// create a Request Request
Request request = new Request.Builder()
    .url("http://www.baidu.com")
    .build();
// create a Call to initiate a network request
Call call = client.newCall(request);

//4

Call the Call execute() method
try {
    // A Response is received
    Response response = call.execute();
    Log.d(TAG, response.body().string());
} catch (IOException e) {
    e.printStackTrace();
}

Call enqueue()
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {}@Override
    public void onResponse(Call call, Response response) throws IOException {
        // A Response is receivedLog.d(TAG, response.body().string()); }});Copy the code

As you can see, there are four steps to making a web request using OKHTTP:

  • Create OkHttpClient
  • Create a Request Request
  • Create a Call from OkHttpClient and Request to initiate a network Request
  • 4. Call the execute() or enqueue() methods of the Call to initiate synchronous or asynchronous requests

When the server finishes processing a Request Request, it will return a Response. In OKHTTP, Response stands for HTTP Response. This is a typical HTTP Request/Response flow. Steps 1 to 3 are introduced as follows:

Create OkHttpClient

OkHttpClient is the big manager of OKHTTP, it distributes the specific work to each subsystem to complete, it uses the Builder mode to configure the network request parameters such as timeout, interceptor, distributor, etc., the parameters can be configured in the Builder as follows:

//OkHttpClient.Builder
public static final class Builder {
    Dispatcher dispatcher;/ / dispenser
    @Nullable Proxy proxy;/ / agent
    List<Protocol> protocols;// Application layer protocol
    List<ConnectionSpec> connectionSpecs;// Transport layer protocol
    final List<Interceptor> interceptors = new ArrayList<>();// Apply interceptor
    final List<Interceptor> networkInterceptors = new ArrayList<>();// Network interceptor
    EventListener.Factory eventListenerFactory;// HTTP request callback listening
    ProxySelector proxySelector;// Proxy selection
    CookieJar cookieJar;//cookie
    @Nullable Cache cache;// Network cache
    @Nullable InternalCache internalCache;// Internal cache
    SocketFactory socketFactory;/ / socket factory
    @Nullable SSLSocketFactory sslSocketFactory;// Socket factory for HTTPS
    @Nullable CertificateChainCleaner certificateChainCleaner;// Verify the confirm response certificate, which applies to the host name of the HTTPS request connection
    HostnameVerifier hostnameVerifier;// Confirm the host name
    CertificatePinner certificatePinner;/ / certificate chain
    Authenticator proxyAuthenticator;// Proxy authentication
    Authenticator authenticator;// Local authentication
    ConnectionPool connectionPool;// Connection pool, multiplexing connections
    Dns dns;/ / domain name
    boolean followSslRedirects;// SSL redirection
    boolean followRedirects;// Local redirect
    boolean retryOnConnectionFailure;// Error reconnection
    int callTimeout;// Request timeout, which includes DNS resolution, connect, read, write, and server processing time
    int connectTimeout;/ / connect timeout
    int readTimeout;/ / read timeout
    int writeTimeout;/ / write timeout
    int pingInterval;/ / ping timeout

    // Here are the default configuration parameters
    public Builder(a) {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;/ / Protocol. HTTP_2 and Protocol. HTTP_1_1
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      if (proxySelector == null) {
        proxySelector = new NullProxySelector();
      }
      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;
      callTimeout = 0;
      connectTimeout = 10 _000;
      readTimeout = 10 _000;
      writeTimeout = 10 _000;
      pingInterval = 0;
    }

    // Here is another OkHttpClient configuration parameter
    Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      / /...
    }
    
    / /...
    
    // After the parameters are configured, create an OkHttpClient using the parameters of Builder
    public OkHttpClient build(a) {
        return new OkHttpClient(this); }}Copy the code

Create a Request Request

In okHTTP, Request represents an HTTP Request, which encapsulates the specific message of the Request, such as URL, header, body, etc. It uses Budiler mode to configure its parameters like OkHttpClient, as follows:

//Request.Budiler
public static class Builder {
    HttpUrl url;
    String method;
    Headers.Builder headers;
    RequestBody body;

    // Set the default parameters here
    public Builder(a) {
      this.method = "GET";// The default is GET request
      this.headers = new Headers.Builder();
    }

    // Here is another Request configuration parameter
    Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      / /...
    }
    
    / /...
    
    // After the parameters are configured, create a Request from the parameters of the Builder
    public Request build(a) {
        if (url == null) throw new IllegalStateException("url == null");
        return new Request(this); }}Copy the code

Create a Call to initiate a network request

Call is an interface, and its concrete implementation class is RealCall, which defines some key methods such as enqueue(Callback) and execute(), as follows:

public interface Call extends Cloneable {
    Return the current request
    Request request(a);
    // Synchronizes the request method, which blocks the current thread until the request result is put back
    Response execute(a) throws IOException;
    // Asynchronous request method, which adds the request to the queue and waits for it to return
    void enqueue(Callback responseCallback);
    // Cancel the request
    void cancel(a);
	// Determine whether the request is being executed
    boolean isExecuted(a);
    // Determine whether the request is cancelled
    boolean isCanceled(a);
    // Return the timeout of the request
    Timeout timeout(a);
    // Clone a new request
    Call clone(a);
    interface Factory {
        Call newCall(Request request); }}Copy the code

OkHttpClient implements the Call.Factory interface, which uses the newCall(Request) method to create a Call. Rewrites the newCall(Request) method to return the Call implementation class RealCall as follows:

public class OkHttpClient implements Cloneable.Call.Factory.WebSocket.Factory {
    / /...
    @Override 
    public Call newCall(Request request) {
        // Call RealCall newRealCall()
        return RealCall.newRealCall(this, request, false /* for web socket */); }}final class RealCall implements Call {
    / /...
    static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) 	{
        // Return the RealCall object
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        call.transmitter = new Transmitter(client, call);
        returncall; }}Copy the code

So a call to **client.newCall(Request)** actually returns a RealCall object, which encapsulates the call logic of the request.

Okhttp initiates synchronous or asynchronous requests through RealCall’s execute() or enqueue() methods, which are the focus of this article, as described below:

Synchronous request – RealCall :: execute()

//RealCall.java
@Override
public Response execute(a) throws IOException {
    / /...
    try {
        Executed (RealCall
        client.dispatcher().executed(this);
        / / 2, call getResponseWithInterceptorChain () method
        return getResponseWithInterceptorChain();
    } finally {
        //3. Invoke the Finished (RealCall) function of the Dispatcher
        client.dispatcher().finished(this); }}Copy the code

Client is an instance of OkHttpClient that was passed as a construct parameter when the RealCall was created. OkHttpClient’s dispatcher() method returns a Dispatcher instance. It is created when the OkHttpClient is constructed.

Let’s talk about Dispatcher first. What is Dispatcher? The Dispatcher is a task scheduler that is responsible for scheduling the requested tasks, It maintains 3 task queues (readyAsyncCalls, runningAsyncCalls, and runningSyncCalls) and 1 thread pool (executorService). The Dispatcher is as follows:

public final class Dispatcher {
    private int maxRequests = 64;// Maximum number of requests is 64
    private int maxRequestsPerHost = 5;// The maximum number of requests per host is 5
    private @Nullable Runnable idleCallback;// Idle task callback, similar to Android idleHandler, can execute tasks in idleCallback when the Dispatcher has no task scheduling (idle)

    // Thread pool that executes request tasks in the runningAsyncCalls queue
    private @Nullable ExecutorService executorService;

    // Queue of asynchronous request tasks waiting to be executed
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    // Queue of asynchronous request tasks in progress
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    // Queue of synchronizing request tasks in progress
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

    synchronized void executed(RealCall call) {
        / /...
    }

    void enqueue(AsyncCall call) {
        / /...
    }  
    
    void finished(RealCall call) {
        / /...
    }

    void finished(AsyncCall call) {
        / /...
    }
    
    private boolean promoteAndExecute(a) {
        / /...
    }

  / /...
}
Copy the code

The Dispatcher provides the executed(RealCall) and enQueue (AsyncCall) methods to queue synchronous and asynchronous requests. Okhttp also provides FINISHED (RealCall) and FINISHED (AsyncCalll) methods for queueing synchronous and asynchronous requests. You can see that OKHTTP uses ReadCall as a representative for synchronous request tasks and AsyncCall as a representative for asynchronous request tasks. AsyncCal is an internal class of RealCall, which is essentially a Runnable, Dispatcher thread pool that performs tasks that are asynchronous requests in the runningAsyncCalls queue. The Dispatcher promoteAndExecute() method is used to dispatch asynchronous tasks. The readyAsyncCalls queue sends asynchronous tasks to the runningAsyncCalls queue in order to be executed by the thread pool. For synchronous tasks, the Dispatcher is temporarily stored in the runningSyncCalls queue. It is not executed by the thread pool.

Let’s go back to the execute() method of RealCall and explain the synchronous request process in three parts according to comments 1, 2, and 3, as follows:

Dispatcher :: Executed (RealCall)

The executed(RealCall) method of the Dispatcher is executed(RealCall).

//Dispatcher.java
synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}
Copy the code

As you can see, no processing is done, simply putting the synchronous request task into the runningSyncCalls queue.

2, RealCall: : getResponseWithInterceptorChain ()

See RealCall the execute () method of 2, call getResponseWithInterceptorChain () method, here is the synchronization request processing place, we go in, as follows:

//RealCall.java 
Response getResponseWithInterceptorChain(a) throws IOException {
    // Add interceptors
    List<Interceptor> interceptors = new ArrayList<>();
    // Add a user-defined interceptor
    interceptors.addAll(client.interceptors());
    // Add a default interceptor
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if(! forWebSocket) { interceptors.addAll(client.networkInterceptors()); }// The last interceptor added is CallServerInterceptor
    interceptors.add(new CallServerInterceptor(forWebSocket));

    // Create a RealInterceptorChain, passing in interceptors and Request
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null.0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    try {
      Call the method proceed(Request) on the RealInterceptorChain to process the Request
      Response response = chain.proceed(originalRequest);
      / /...
      return response;
    } catch (IOException e) {
     / /...
    } finally {
     / /...}}Copy the code

GetResponseWithInterceptorChain () method returns a Response finally, also is the Response of the network request, the method of first turn on the user-defined interceptors and okhttp default interceptor wrapped in a List, Then create the RealInterceptorChain and execute the proceed(Request) method to handle the Request. The RealInterceptorChain’s proceed(Request) method is as follows:

//RealInterceptorChain.java
@Override 
public Response proceed(Request request) throws IOException {
    return proceed(request, transmitter, exchange);
 }

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
    throws IOException {
    / /...
    // Create a new RealInterceptorChain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
                                                         index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    // Gets the next interceptor in the list of interceptors
    Interceptor interceptor = interceptors.get(index);
    // Call the intercept(Chain) method of the next interceptor, pass in the newly created RealInterceptorChain, and return Response
    Response response = interceptor.intercept(next);
    / /...

    return response;
}
Copy the code

The proceed() method creates a new RealInterceptorChain, passing in index + 1, and retrieving the interceptor using index. This method obtains the next interceptor each time, and then calls the intercept(Chain) method of the next interceptor. The intercept(Chain) method is the main implementation of the interceptor. It continues to call the incoming RealInterceptorChain’s proceed() method, which repeats the above logic. Consider the interceptor as a node in a Chain. This is the typical chain of responsibility pattern. The request is passed from the head of the node, and each interceptor has a chance to process the request. This is a recursive process until the last interceptor has finished processing the request. The interceptors are the core functionality of Okhttp, and will be covered in the next article. Just know that each interceptor represents a function.

After a brief introduction of the interceptor, we know that the last add interceptor is put the request is sent out and returns a response, we look at getResponseWithInterceptorChain () method, the last is a blocker to add CallServerInterceptor, CallServerInterceptor intercept(Chain)

//CallServerInterceptor.java
@Override
public Response intercept(Chain chain) throws IOException {
    // Strongly converted to RealInterceptorChain
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    / / for Exchange
    Exchange exchange = realChain.exchange();
    / / get Request
    Request request = realChain.request();

	//1. Send the request header using Exchange's writeRequestHeaders(request) method
    exchange.writeRequestHeaders(request);

    boolean responseHeadersStarted = false;
    Response.Builder responseBuilder = null;
    // As mentioned earlier, the default is a GET request, and the GET request has no body, so it does not enter the if branch
    if(HttpMethod.permitsRequestBody(request.method()) && request.body() ! =null) {
      // Omit the body of the Request
      / /...
    } else {
      exchange.noRequestBody();
    }

    //GET request body is empty, go to the branch, complete the request
    if (request.body() == null| |! request.body().isDuplex()) { exchange.finishRequest(); }// Some listener callbacks are omitted
    / /...
    
    // Start getting the response from the network request
    
    // Use the Exchange readResponseHeaders(Boolean) method to obtain the header of the response
    if (responseBuilder == null) {
        responseBuilder = exchange.readResponseHeaders(false);
    }
    
    // After getting the Response, construct the Response with Builder mode
    Response response = responseBuilder
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    // omit the response to the status code processing
    / /...
    
    // Construct the body of Response
    if (forWebSocket && code == 101) {
        // Construct an empty body Response
        response = response.newBuilder()
            .body(Util.EMPTY_RESPONSE)
            .build();
    } else {
        // Get the body of the Response through the Exchange openResponseBody(Response) method, and then proceed to construct the Response through the body of the Response
        response = response.newBuilder()
            .body(exchange.openResponseBody(response))
            .build();
    }
    
    / /...

    // Return Response Response
    return response;
  }
Copy the code

Intercept (Chain) : Intercept (Chain) : intercept(Chain) : intercept(Chain) : Intercept (Chain) : Intercept (Chain) : Intercept (Chain) : Intercept (Chain) The Exchange object is passed into the RealInterceptorChain as a construction parameter. The Exchange object is null when the RealInterceptorChain is constructed. Only in the ConnectInterceptor intercept() is the Transmitter newExchange() attached, and the next Transmitter in the ConnectInterceptor is the CallServerInterceptor. So the CallServerInterceptor can get an Exchange instance through the Chain. Exchange is used to complete a network request and response.

Here I use the intercept(Chain) method of comment 1 and comment 2 Request header send (wirte) and obtain (read) as an example to understand the working process of Exchange, first look at Exchange writeRequestHeaders(Request) method, As follows:

//Exchange.java
public void writeRequestHeaders(Request request) throws IOException {
    try {
        // Call codec's writeRequestHeaders(request)
        codec.writeRequestHeaders(request);
        / /...
    } catch (IOException e) {
        / /...}}Copy the code

Exchange readResponseHeaders(Boolean)

//Exchange.java
public @Nullable Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
    try {
      //主要是调用了codec的readResponseHeaders(boolean)
      Response.Builder result = codec.readResponseHeaders(expectContinue);
      / /...
      return result;
    } catch (IOException e) {
     / /...}}Copy the code

Exchange’s two methods delegate wirt and read header tasks to codec. What is coDEC? Codec is an Exchange Dec type. It’s an interface that encodes HTTP requests and decodes HTTP returns. So Exchange Dec does the real work and has two implementation classes. Http2ExchangeCodec and Http1ExchangeCodec correspond to http2.x and http1.x respectively. Here we use Http1ExchangeCodec as an example. See the writeRequestHeaders(Request) and readResponseHeaders(Boolean) methods for Http1ExchangeCodec writeRequestHeaders(Request) :

//Http1ExchangeCodec.java
@Override 
public void writeRequestHeaders(Request request) throws IOException {
    String requestLine = RequestLine.get(
        request, realConnection.route().proxy().type());
    // call writeRequest()
    writeRequest(request.headers(), requestLine);
  }

 public void writeRequest(Headers headers, String requestLine) throws IOException {
    if(state ! = STATE_IDLE)throw new IllegalStateException("state: " + state);
    // Write the request header to the IO stream with sink and send it to the server. Sink is of type BufferedSink
    sink.writeUtf8(requestLine).writeUtf8("\r\n");
    for (int i = 0, size = headers.size(); i < size; i++) {
      sink.writeUtf8(headers.name(i))
          .writeUtf8(":")
          .writeUtf8(headers.value(i))
          .writeUtf8("\r\n");
    }
    sink.writeUtf8("\r\n");
    state = STATE_OPEN_REQUEST_BODY;
  }
Copy the code

ReadResponseHeaders (Boolean) http1Exchange Dec readResponseHeaders(Boolean)

//Http1ExchangeCodec.java
@Override
public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
   / /...
    try {
        StatusLine statusLine = StatusLine.parse(readHeaderLine());
        Response.Builder responseBuilder = new Response.Builder()
            .protocol(statusLine.protocol)
            .code(statusLine.code)
            .message(statusLine.message)
            .headers(readHeaders());// Call readHeaders()
        / /...
        return responseBuilder;
    } catch (EOFException e) {
        / /...}}private Headers readHeaders(a) throws IOException {
    Headers.Builder headers = new Headers.Builder();
    ReadHeaderLine () is called, reading the headers line by line
    for(String line; (line = readHeaderLine()).length() ! =0;) { Internal.instance.addLenient(headers, line); }return headers.build();
  }

 private String readHeaderLine(a) throws IOException {
     // The server returns the response and reads the response header from the IO using source, which is of type BufferedSource
    String line = source.readUtf8LineStrict(headerLimit);
    headerLimit -= line.length();
    return line;
  }

Copy the code

As you can see from the two methods of Http1ExchangeCodec, the underlying message is written to the IO stream by BufferedSink and read from the IO stream by BufferedSource. Both BufferedSink and BufferedSource are from okio, an open source library that uses OKHTTP to write and read IO to the network. For more information, check out okio source code (Okio is also a square product).

Finish to the RealCall getResponseWithInterceptorChain () analysis, getResponseWithInterceptorChain () returns the Response, RealCall’s execute() method returns, and we can retrieve the desired information in Response, but RealCall’s execute() method returns, and then continues to execute the logic in the finally branch.

3, Dispatcher :: finished(RealCall)

Continuing with RealCall’s execute() method in comment 3, we call the Finished (AsyncCall) method of the Dispatcher as follows:

//Dispatcher.java
void finished(RealCall call) {
    // The runningSyncCalls queue is passed
    finished(runningSyncCalls, call);
}

private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
        // Try to remove the synchronization request task from the queue
        if(! calls.remove(call))throw new AssertionError("Call wasn't in-flight!");
        idleCallback = this.idleCallback;
    }

    // The promoteAndExecute() method is then called to schedule asynchronous tasks. If there are no asynchronous tasks to execute, promoteAndExecute() returns false
    boolean isRunning = promoteAndExecute();

    //isRunning is false and idleCallback is set to idle
    if(! isRunning && idleCallback ! =null) { idleCallback.run(); }}Copy the code

The finished() method first attempts to remove the RealCall task that was executed() from the runningSyncCalls queue. If the deletion fails, an exception is thrown. Then the promoteAndExecute() method is called to schedule the asynchronous task and try to execute the idle task. The promoteAndExecute() method is described in more detail in the asynchronous request.

summary

The okHTTP synchronization request process is analyzed and summarized here: When we call call.execute(), we make a synchronous request, and the call implementation class is RealCall, so we actually execute realCall.execute(), Executed (realCall) of the Dispatcher saves the synchronization request to the runningSyncCalls queue. Then RealCall execution getResponseWithInterceptorChain () synchronous request, the request after layers of interceptor arrive last interceptor CallServerInterceptor, through Exchange in the interceptor to send the request to a server, And then again get the Response from the server through Exchange, construct a Response based on the Response, and return, Finally, RealCall executed the Dispatcher finished(RealCall) to remove temporarily saved synchronization request tasks from the runningSyncCalls queue.

Here is the call chain for the synchronous request process:

Asynchronous request – realCall.enqueue (Callback)

//RealCall.java
@Override
public void enqueue(Callback responseCallback) {
    / /...
    Call Dispatcher enqueue(AsyncCall)
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Copy the code

An asynchronous request executes RealCall’s enQueue (Callback) method, which has only one more Callback than a synchronous request. In the onResponse(Call, Response) Callback, we can retrieve the Response returned by the network Response. RealCall enQueue (Callback) wraps Callback with AsyncCall and calls enQueue (AsyncCall) which calls Dispatcher.

Dispatcher :: enqueue(AsyncCall)

Dispatcher enqueue(AsyncCall);

//Dispatcher.java
void enqueue(AsyncCall call) {
    synchronized (this) {
       readyAsyncCalls.add(call);
       / /...
    }
    promoteAndExecute();
}

Copy the code

This method first places AsyncCall in the readyAsyncCalls queue and then calls promoteAndExecute() to schedule the asynchronous task. Let’s see how the Dispatcher dispatches the asynchronous task.

2, promoteAndExecute()

PromoteAndExecute () method is as follows:

//Dispatcher.java
private boolean promoteAndExecute(a) {
    // Prepare a list of executableCalls tasks
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
        // The for loop mainly transfers asynchronous tasks from readyAsyncCalls to the runningAsyncCalls queue and executableCalls list
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {

            // Retrieve the asynchronous task waiting to execute in readyAsyncCalls
            AsyncCall asyncCall = i.next();

            // The number of asynchronous requests running cannot be larger than maxRequests. 2. The number of host requests for asynchronous tasks to be executed cannot be greater than maxRequestsPerHost
            if (runningAsyncCalls.size() >= maxRequests) break; 
            if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue;
            // If the condition is met, enter the following logic

            // Remove the asynchronous task waiting to execute from readyAsyncCalls
            i.remove();
            asyncCall.callsPerHost().incrementAndGet();
            
            // Add the asynchronous task waiting to execute to the executableCalls list
            executableCalls.add(asyncCall);
            // Add the asynchronous task to the runningAsyncCalls queue
            runningAsyncCalls.add(asyncCall);
        }
        
        //runningCallsCount() : return runningAsynccalls.size () + RunningSynccalls.size ();
        isRunning = runningCallsCount() > 0;
    }
    // The for loop performs asynchronous tasks from the executableCalls list
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
        AsyncCall asyncCall = executableCalls.get(i);
        ExecutorService, call AsyncCall's executeOn() method, and the thread pool executes the asynchronous task
        asyncCall.executeOn(executorService());
    }

    return isRunning;
}
Copy the code

The promoteAndExecute() method mainly has two for cycles, Note 1’s first for loop transfers (promotes) qualified asynchronous request tasks from readyAsyncCalls to the runningAsyncCalls queue and adds them to the executableCalls list. The second for loop immediately following Note 2 is to traverse the executableCalls list, fetching AsyncCall objects from the executableCalls list, and calling its executeOn() method, The executeOn() method loads into a Dispatcher’s executorService, so let’s look at AsyncCall’s executeOn() method, which is where the asynchronous request task is actually executed.

2.1、AsyncCall :: executeOn(ExecutorService)

AsyncCall’s executeOn() method looks like this:

//AsyncCall.java
void executeOn(ExecutorService executorService) {
    boolean success = false;
    try {
        // Pass this to perform AsyncCall tasks. AsyncCall is essentially Runnable
        executorService.execute(this);
        success = true;
    } catch (RejectedExecutionException e) {
       / /...
    } finally {
        if(! success) {// Asynchronous task execution fails. Call the Finished (AsyncCall) method of the Dispatcher
            client.dispatcher().finished(this); }}Copy the code

Execute (this). AsyncCall implements NamedRunnable, which is essentially Runnable, as follows:

final class AsyncCall extends NamedRunnable {
    / /...
}

public abstract class NamedRunnable implements Runnable {
	/ /...
  @Override
    public final void run(a) {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      // Execute () is executed in the run method
      execute();
    } finally{ Thread.currentThread().setName(oldName); }}protected abstract void execute(a);
}

Copy the code

When the thread pool executes this asynchronous task, its run method is executed. The run method mainly calls execute(), which is an abstract method. AsyncCall implements NamedRunnable. So AsyncCall overrides execute() to implement the execution logic, so let’s go straight to AsyncCal’s execute() method.

2.2、AsyncCal :: execute()

AsyncCal’s execute() method is as follows:

//AsyncCall.java
@Override 
protected void execute(a) {
    / /...
    try {
        / / call RealCall getResponseWithInterceptorChain () method processes the request
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        // The onResponse() method of Callback is called
        responseCallback.onResponse(RealCall.this, response);
    } catch (IOException e) {
        / /...
    } finally {
        // Call the Finished (AsyncCall) method of the Dispatcher
        client.dispatcher().finished(this); }}Copy the code

AsyncCal the execute () method of logic and introduces the process of synchronous request all roads lead to Rome, in front of the first call of RealCall getResponseWithInterceptorChain () method processes the request, the request processing is completed, return to Response the Response, The onResponse() method of Callback passed in when we Call call.enqueue (Callback), The finished(AsyncCall) method of the Dispatcher is called in the finally statement to remove the asynchronous request task from the runningAsyncCalls queue using the same removal logic as the synchronous request task above. Only this time it is removed from runningAsyncCalls instead of runningSyncCalls as follows:

//Dispatcher.java
void finished(AsyncCal call) {
    // Pass runningAsyncCalls instead of runningSyncCalls
    finished(runningSyncCalls, call);
}
Copy the code

summary

When we call call.enqueue(Callback), an asynchronous request is issued, and realCall.enqueue(Callback) is actually executed. Realcall.execute () wraps the incoming Callback as an AsyncCall, Then execute enQueue (AsyncCall) of Dispatcher to save the async calls to readyAsyncCalls queue, and then execute promoteAndExecute() to schedule the async calls. It will first transfer qualified asynchronous request tasks from readyAsyncCalls to the runningAsyncCalls queue and add them to the executableCalls list, then traverse the executableCalls list, ExecutorService AsyncCall ExecutorService (ExecutorService) one by one, then AsyncCall will put itself into the Dispatcher’s thread pool and wait for the thread pool to dispatch. When the thread pool executes to the AsyncCall, Its run method is executed to execute the overridden execute() method, which has roughly the same flow as a synchronous request.

Here is the call chain for the asynchronous request process:

conclusion

Okhttp creates OkHttpClient, Request, and Response in Builder mode, and creates a Call from client.newCall(Resquest) for an asynchronous or synchronous Request that goes through a Dispatcher, a series of interceptors, Finally, okIO establishes a connection with the server, sends data and parses the returned result. The process is shown in the figure below:

This is the okHTTP request flow analysis, if any errors, welcome to point out.

Reference article:

OkHttp 3.x source code parsing Dispather distributor