OKHttp source code analysis 2: synchronous request and asynchronous request process and source code analysis

foreword

The previous chapter has explained the basic usage of OKHttp's synchronous and asynchronous requests. In general, it is divided into four steps. The first three steps are the same, only the fourth step has the distinction of calling methods. The execute() method of Call is called synchronously, and the enqueue() method of Call is called asynchronously. Now we will go deep into the source code step by step according to the calling process of these four steps, and see what OKHttp has done.

First of all, the version of OKHttp I am using now is . In com.squareup.okhttp3:okhttp:3.10.0the process of tracking the source code, I will try my best to post the source code of each step, so that you can read the source code while reading the article. Of course, it is also recommended that you open Android Studio and follow I follow the source code together, because then you will have a more comprehensive and in-depth understanding of the source code. Well, without further ado, let's get straight to the point.

When OKHttpClient is created, what does it mainly do?

First, we start with the creation of OKHttpClient,

//创建OkHttpClient,这里只简单设置了超时时间为10
OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(10, TimeUnit.SECONDS).build();

As you can see here, OKHttpClient is created through the Builder mode, now we enter the Builder() method, the source code is as follows:

public Builder() {
    //OKHttp核心:分发器类
    dispatcher = new Dispatcher();
    protocols = DEFAULT_PROTOCOLS;
    connectionSpecs = DEFAULT_CONNECTION_SPECS;
    eventListenerFactory = EventListener.factory(EventListener.NONE);
    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;
    pingInterval = 0;
}

In the Builder construction method, the two most important classes are Dispatcher and ConnectionPool. I have added comments to the source code respectively.

Among them, Dispatcher is the core class of OKHttp: the dispatcher class, which determines whether the asynchronous request is directly added to the request queue or added to the waiting queue. Of course, synchronous requests are also controlled by it, but they are not as complicated as asynchronous requests. In the following chapters, I will focus on the analysis of this class, here is only a brief functional description.

The other is ConnectionPool, which is the connection pool of OKHttp and a very important concept in OKHttp. It mainly plays the role of multiplexing http connections and managing http connections, and it will be explained in the following chapters.

In addition to these two important parameters, there are various other parameters in the Builder construction method. They are all initialized in this construction method, and then pass the Builder object to the OKHttpClient construction method to complete the initialization of various parameters of OKHttpClient. .

//通过Builder()构造方法对各参数进行初始化之后,调用build()方法,传递Builder对象
//(this关键字代表的就是Builder对象)到OKHttpClient中,完成OKHttpClient对象的创建
public OkHttpClient build() {
    return new OkHttpClient(this);
}

This is the Builder construction mode. When there are many parameters that need to be initialized, we can use this mode to create objects. It is used in many large open source frameworks and can also be used in our daily business development. At this point, OKHttpClient is created, and then we analyze the creation of Request.

Creation of Request

//创建Request
Request request = new Request.Builder().url("http://www.baidu.com").build();

It can be found that Request is also created through the Builder mode, entering the Builder() construction method

public Builder() {
    this.method = "GET";
    this.headers = new Headers.Builder();
}

The content is very simple, only the Builder object of the method and the request header Header is initialized. Next we look at the build() method:

public Request build() {
    if (url == null) throw new IllegalStateException("url == null");
    return new Request(this);
}

It is very simple, just like the creation of OKHttpClient, pass the Builder object to the constructor of Request, and return a Request object. The source code of the construction method of Request is:

Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
}

Through the construction method of Request, we can find that when Request is created, a series of parameters such as URL, method, request header, and body are initialized. At this point, synchronous and asynchronous requests have completed the first two steps. Next, we analyze the creation of the third step Call.

Call creation process analysis

First of all, through the previous study, we know that the creation of Call is created by calling the newCall() method of OKHttpClient, and we enter the source code of this method

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

It can be found that since Call is an interface, the specific implementation is actually in the newRealCall() method of the RealCall implementation class of Call. We enter the source code of this method.

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

Very simple, create a RealCall object and return it. You can also enter the constructor of RealCall to view the specific content. At this point, the Call object has been created successfully.

At this point, the first three steps have been analyzed. In fact, the first three steps are just some preliminary preparations for initiating the request. The fourth step is initiating the request, so next, we have arrived at the most important content of this chapter. It is the fourth step to distinguish between OKHttp synchronous and asynchronous requests.

Source code analysis of OKHttp synchronous and asynchronous requests

Synchronous request method execute() source code analysis

First, let's take a look at the source code of the synchronous request method execute()

@Override 
public Response execute() throws IOException {
    synchronized (this) {
      //这里通过executed参数判断同一个请求是否已经执行,如果已经执行,则抛出异常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    //收集异常堆栈信息
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      //执行同步请求
      client.dispatcher().executed(this);
      //重要!!通过拦截链获取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);
    }
  }

In the first comment position, that is, in the synchronized synchronization request block, by judging the executed flag parameter, the effect achieved is: for the same request, if it has been executed, an exception will be thrown, otherwise, executed is set to true. Next, let's look at the third comment position. By calling the executed() method of the Dispatcher to execute the synchronous request of OKHttp, we enter the executed() source code

/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
    //添加 RealCall 到同步请求队列
    runningSyncCalls.add(call);
}

Here, RealCall is added to the synchronous request queue, and this synchronous request queue runningSyncCalls is initialized in Dispatcher, which is a very important knowledge point in OKHttp Dispatcher dispatcher. In the next chapter, I will discuss about The Dispatcher source code is analyzed in detail, which is briefly mentioned here. Along with the synchronous request queue, there are the asynchronous request queue runningAsyncCalls and the asynchronous request waiting queue readyAsyncCalls used in the asynchronous request. The source code is as follows

/** 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<>();

After executing the synchronous request, the next step is the getResponseWithInterceptorChain() method. This method is used to obtain the Response of the response. This method is also a core point of OKHttp. The interceptor will be called in turn to perform the corresponding operation, which will be opened later. A separate chapter explains it.

Finally, in finally, we can see a very important mechanism, which is the finished() method. It will actively recycle synchronization requests, let's take a look at the source code

/** Used by {@code Call#execute} to signal completion. */
  void finished(RealCall call) {
    //调用下面的 finished 方法,注意 false 参数
    finished(runningSyncCalls, call, false);
  }

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      //把前面传入的同步请求移出同步请求队列,不能移出则抛出异常
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      //同步请求时,promoteCalls为false,这里不会执行
      if (promoteCalls) promoteCalls();
      //计算请求的数量
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }
    //当剩余请求数量为0,执行 idleCallback 的 run 方法
    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

Its first operation is the first line of code in the synchronized block, which removes the request from the synchronization request queue, or throws an exception if it cannot be removed. Then because the incoming promoteCalls is false, it will not go to the promoteCalls() method in a synchronous request, but it will come here in an abnormal request. The next thing to do is to calculate the total number of requests that still exist. The specific source code is as follows

public synchronized int runningCallsCount() {
    //返回异步请求队列与同步请求队列的总和
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }

Finally, it is judged that if runningCallsCount==0, that is, the synchronous and asynchronous request queues in Dispatcher are empty, and idleCallback is not empty, execute the run method of idleCallback. At this point, the entire process of the synchronization request is analyzed. It can be found that the operation of Dispatcher on synchronization requests is very simple, that is, adding synchronization requests to the synchronization request queue, and removing synchronization requests from the synchronization request queue. Next, we analyze the process of the asynchronous request method enqueue().

Asynchronous request method enqueue() source code analysis

First, we trace the Call.enqueue() method. Because Call is an interface, we locate the RealCall implementation class of Call, and then find the enqueue() method in RealCall. The source code is as follows

 @Override 
 public void enqueue(Callback responseCallback) {
    synchronized (this) {
      // 首先判断这个Call请求是否已经执行过,执行过抛出异常,否则执行该 Call 请求
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    //新建 AsyncCall 对象,通过 Dispatcher 的 enqueue() 方法执行该 Call 请求
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

The first thing done inside this method, like the synchronous request method execute(), is to judge whether the current Call request has been executed, and go down if it is not executed. Then build an AsyncCall object by passing in the Callback object, pay attention to the AsyncCall class, let's take a look at its source code

final class AsyncCall extends NamedRunnable {
    ...
}

Looking at the source code, we found that AsyncCall inherits the NamedRunnable class. We can probably guess that this is a Runnable. We continue to track the source code of the NamedRunnable class.

public abstract class NamedRunnable implements Runnable {
    ...

    @Override 
  public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

Here we find that NamedRunnable is indeed a Runnable. After constructing the object of Runnable's implementation class AsyncCall, the enqueue() method of Dispatcher is called next, and the logic of enqueue() method can be found through the source code: first, determine whether the currently executing asynchronous request is less than The maximum allowed (64), and whether the host of an executing asynchronous request is less than the maximum allowed (5). If the conditions allow, add the Call request to the executing request queue, and notify the thread pool to execute the request; if the conditions do not allow, add the Call request to the asynchronous waiting queue, and then proceed to the next step.

//最大的请求数
private int maxRequests = 64;
//最大的请求 host 数
private int maxRequestsPerHost = 5;

/** 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<>();

//同步锁
synchronized void enqueue(AsyncCall call) {
    //判断正在执行的异步请求是否小于允许的最大值、以及正在执行的异步请求的 Host 是否小于允许的最大值
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      //条件允许,则把 Call 请求加入到正在执行的请求队列中
      runningAsyncCalls.add(call);
      //通知线程池执行这个请求
      executorService().execute(call);
    } else {
      //条件不允许,则把 Call 请求加入到异步等待队列中
      readyAsyncCalls.add(call);
    }
  }

Next we will focus on executorService().execute(call);this code. We know that the reuse of this code is to notify the thread pool to execute the request, that is, call needs to execute its run() method, and call is an implementation class of Runnable. Earlier we found by tracing the source code that in NamedRunnable, the run() method is very simple, but it provides an abstract execute() method for AsyncCall to implement, so we focus on the execute() method in AsyncCall.

@Override 
protected void execute() {
    boolean signalledCallback = false;
    try {
      //通过拦截链获取请求 Response
      Response response = getResponseWithInterceptorChain();
      //通过判断 retryAndFollowUpInterceptor 拦截器是否取消,返回相应 callback 方法的回调
      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 {
      //主动finish掉请求
      client.dispatcher().finished(this);
    }
}

First of all, we encountered the aforementioned, a very important content in OKHttp, getResponseWithInterceptorChain(), the interception chain. This will be analyzed in a later article. Here we only need to know that whether it is a synchronous request or an asynchronous request, the Response is obtained through the interception chain. Next, by judging whether the retryAndFollowUpInterceptor interceptor is canceled, different methods are called to the Callback, and the callback of the asynchronous request is returned from here. Finally, in finally, it is also the active finish request mentioned above. So far, the whole asynchronous request calling process has been analyzed, we summarize as follows:
1. Create an OKHttpClient and Request object
2. Through OKHttpClient and Request object, construct the Call object of the actual http request, and execute the enqueue() method of the Call object
3. Then build the implementation class AsyncCall of Runnable, pass it to the enqueue() method of Dispatcher through the dispatcher Dispatcher. If the asynchronous request queue size in Dispatcher is less than the maximum value of 64 and the asynchronous waiting queue size is less than the maximum value of 5, add the current Call request to the asynchronous request queue, and then notify the thread pool to execute the request. Otherwise, join the waiting queue for follow-up operations
. 4. The execution of AsyncCall occurs in the execute() method of AsyncCall. The Response is obtained through the interceptor chain, and then different methods of callback are called to return the corresponding results, and finally the request is actively finished.

Summarize

At this point, the main process and source code analysis of the entire OKHttp synchronous and asynchronous request is finished, which involves two very important knowledge points in OKHttp: the dispatcher Dispatcher and the interceptor chain getResponseWithInterceptorChain(), which will be explained in the following articles. . Finally, I compiled a set of UML class diagrams for synchronous and asynchronous requests, so that we can understand the calling of this process and the thinking of the framework from an overall perspective.
okhttp synchronous and asynchronous request flow chart

Next articleOKHttp source code analysis 3: Dispatcher analysis of the core class of task scheduling

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325222638&siteId=291194637