Analysis of the basic principle of OkHttp

Analysis of the basic principle of OkHttp

Let's first look at the two methods of using OkHttp
synchronization :

   //同步执行方法,不应该在UI线程使用
    response = client.newCall(request).execute();
    //异步方法    
    response = client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                }
            }
        });

让我们进入newCall一探究竟~


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

    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;
    }

You can see that a RealCall object is returned, and then continue to see what RealCall is, mainly its execute and enqueue methods

  @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);
      }
    }
    }

It can be seen that execute is indeed executed in the thread that calls it. The code should be executed through getResponseWithInterceptorChain(). I saw the familiar interceptor. In fact, here is the interceptor that uses the chain of responsibility design pattern. Besides , the execute method will come to an end for the time being, and then look at enqueue

   @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    //看来这里才是真正执行的方法,继续跟踪,发现了一个dispather,并且还new了一个AsyncCall对象,传入了我们的匿名内部类接口实例
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

A dispather is tracked above, what the hell is this? Literally understood as a distributor, it seems that things are not so simple, continue to follow up

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      //咦,这个不是线程池嘛 
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
    }

We caught the enqueue of the dispather and found that his call was thrown to the thread pool, so what is this call, that is, AsyncCall? Let's see it first

   /**
      *注意这个类在RealCall类里面哦,是其内部类
      */
     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;
    }

    //发现AsyncCall也有一个execute方法,和RealCall差不多
    @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;
          //这个是传进来的回调啦,它不是UI线程
          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);
          //这个也是传进来的回调啦,它也不是UI线程
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
    }

It turns out that it implements NamedRunnable, then this NamedRunnable should implement Runnable, and then our execute is not called here, I guess it should be called by the run rewritten by the parent class, and then this run should be in the thread pool, verify

 public abstract class NamedRunnable implements Runnable {
    protected final String name;
    public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
    }

    @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();
    }

Going back to the enqueued of RealCall, is it a clearer idea?
First, newCall(request) generates a RealCall after initializing the request, and then the execute of RealCall starts to execute the request. If it is asynchronous, it is to pass in enqueue(callback), and then call dispatcher.enqueue(new AsyncCall(callback)), this AsyncCall It is the inner class of RealCall, so it can also access the request initialized by newCall(request). It holds the callback callback and is also a Runnable, and its run method calls its own execute method with the same name as the external class RecalCall and similar functions. , and then this AsyncCall is thrown to the thread pool for execution in the dispather, so the problem is solved.

      @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));
    }

Said dispather for so long? What exactly is this dispather? Don't worry, keep watching

 private int maxRequests = 64;
     private int maxRequestsPerHost = 5;
     ...
     public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
    }

    public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
    }

It turned out to be a wrapper class for a thread pool. . . The core thread of this thread pool is 0, the maximum value of non-core thread int, but the number of concurrent threads has been limited by maxRequest and maxRequestsPerHost outside. The non-core thread has a timeout of 60s, and the blocking queue is SynchronousQueue, which is a queue that does not store elements. In must go out. This problem has been basically solved.

Let's talk about getResponseWithInterceptorChain()

 //这个是真正发起请求的方法
    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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
    }

Continue to look at chain.proceed, and I mentioned the Chain of Responsibility design pattern by the way. The typical responsibility chain mode is to define a responsibility chain node object abstract class and a message abstract class. Each node object must implement the handleMessage(Object message) method of the abstract class, and then process the incoming message according to the actual situation. Those that are processed or cannot be processed are handed over to the next chain node object until the message is processed.

The chain of responsibility embodies the typical layering idea, the benefits of layering are obvious, complex and simple, flexible and easy to maintain. For example, the TCP/IP protocol is a classic example.

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    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");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    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");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
    }

A brief summary of the above code, in fact, is probably to instantiate a chain of responsibility in getResponseWithInterceptorChain(), register various interceptors (chain node objects), and then obtain the next interceptor (node) through chain.proceed

 // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);

After each call to the chain object proceed to obtain the node, the intercept of the node takes out the request processing in the chain, and then the user calls procee, index+1 in it to get the next interceptor, and then calls the interception method intercept of the interceptor. Take out the request processing in the chain and continue the cycle.

Briefly explain the interceptor

  • Application blocker (user added)
  • Interceptors for redirects etc.
  • Interceptor for constructing request headers
  • cache interceptor
  • connection interceptor
  • Network Interceptor (Added by User)
  • data transfer interceptor

Finally, two pictures on the Internet are attached.

OkHttp process
write picture description here

interceptor
write picture description here

Guess you like

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