About OkHttp

OkHttp is an HTTP+HTTP/2 client for Android and Java applications.

How to use

Just add rely on Android development, are as follows: implementation (” com. Squareup. Okhttp3: okhttp: 3.13.1 “)

Official example 1: Get the content of a URL and print it

/ / Http client
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
    // Construct the request
  Request request = new Request.Builder()
      .url(url)
      .build();
  // Execute the request to get the data
  try (Response response = client.newCall(request).execute()) {
    returnresponse.body().string(); }}Copy the code

Official Example 2: Post data to the server

public static final MediaType JSON
    = MediaType.get("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  try (Response response = client.newCall(request).execute()) {
    returnresponse.body().string(); }}Copy the code

A quick look at the code: The POST request operation looks simple. But we need to learn a few important interfaces:

OKHttpClient: it represents the HTTP client. Request: it encapsulates the Request object. You can construct an HTTP Request object. The client.newCall() Call generates a request execution object called, which encapsulates the request execution process.

Here we combine this example to analyze the source code:

newCall().execute()

After tracing the source code, we found that this method is the interface in Call, with the following code:

/** * A call is a request that has been prepared for execution. A call can be canceled. As this object * represents a single request/response pair (stream), it cannot be executed twice. */
public interface Call extends Cloneable {
    // Omit some code
    
    // Execute requests synchronously
    Response execute(a) throws IOException;
    
    // Request to join the queue
    void enqueue(Callback responseCallback);
    
    // Omit some code
}
Copy the code

The Call implementation class is RealCall. Follow the source realcall. Java file and you can see the execute method:

@Override public Response execute(a) throws IOException {
    // The synchronization lock checks whether the request has been executed and if not, the flag executed = ture or an exception is thrown
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    
    captureCallStackTrace();
    timeout.enter();
    // The callback method callStart is called
    eventListener.callStart(this);
    try {
      // The okHTTP client calls the Dispatcher to execute the request object
      client.dispatcher().executed(this);
      / / call the getResponseWithInterceptorChain method to obtain the Response to the Response data, later will continue to analysis
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      e = timeoutExit(e);
      // Failed to request callback callFailed
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
      // Call the finished method using the dispather object to complete the request}}Copy the code

Next, we detailed analyze the dispatcher. The execute and getResponseWithInterceptorChain these two methods:

Trace source Dispatcher

public final class Dispatcher {
    // Omit some code
  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService;

  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if(! call.get().forWebSocket) { AsyncCall existingCall = findExistingCallWithHost(call.host());if(existingCall ! =null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
  }
  
  private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if(! calls.remove(call))throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }
    boolean isRunning = promoteAndExecute();
    if(! isRunning && idleCallback ! =null) { idleCallback.run(); }}}Copy the code

Discover that the Dispatcher is a scheduler whose purpose is to distribute requests. It has three internal queues, namely synchronous request queue, asynchronous request waiting queue, asynchronous request execution queue.

The core method: getResponseWithInterceptorChain

  Response getResponseWithInterceptorChain(a) throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());// Add a custom interceptor
    interceptors.add(retryAndFollowUpInterceptor);// Retry the redirection interceptor
    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));// Invoke server interception

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null.null.null.0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    // Start responsibility chain mode
    return chain.proceed(originalRequest);
  }
Copy the code

After building several interceptors, this approach constructs an interceptor responsibility chain. RealInterceptorChain:

RealInterceptorChain

This class is responsible for concatenating all interceptors so that all interceptors are implemented recursively, ensuring that a Response is returned only after all interceptors have been executed. The following explains the main method in this class, proceed:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
    // Count the number of times the interceptor calls the method
    calls++;
    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec ! =null&&!this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec ! =null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }
    // Generate the next interceptor to invoke the method's RealInterceptorChain object, where index+1 is used to get the next interceptor
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    // Get the current interceptor
    Interceptor interceptor = interceptors.get(index);
    // Call the interceptor and get the return result
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if(httpCodec ! =null && index + 1< interceptors.size() && next.calls ! =1) {
      throw new IllegalStateException("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;
  }
Copy the code

conclusion

That’s our analysis of OkHttp’s core source code. A Call instance is initialized when a request is made, and its execute() and enqueue() methods are called, depending on whether the Call is synchronous or asynchronous. The general process is to retrieve a response and deliver it to the user through a chain of interceptors, retry, bridge, cache, connect, and access the server.