Android深入理解源码——OkHttp篇(上)

声明:原创作品,转载请注明出处https://www.jianshu.com/p/6a1d4773a130

相信做过Android开发的小伙伴对OkHttp一定不陌生,OkHttp现在是网络请求最流行的一个库,出现的很早,早在Android 4.x就出现了,当时这个库只是对Android原生网络api进行一个简单的封装,后来慢慢的直接废弃了原生的api,内部直接使用自己开源的okio来实现。OkHttp能成为如此优秀的一个开源库,肯定有一些值得学习和借鉴的地方,所以今天就带大家一起来看看其内部实现细节,当然我不准备一上来就贴上各种源码,以免搞得晕头转向,当然了OkHttp相关的讲解网上也有很多文章,所以今天我准备换个思路来写OkHttp,假如现在是你要实现一个网络请求库,你会怎么做。我们就顺着这个思路来看下一个网络库或者说一个三方库是如何一步步实现的。

1.需求分解

首先我们先来明确下,我们这个库是要做什么的?很简单就是实现一个网络请求操作,说的具体点就是从客户端发送一个网络请求到服务端,然后从服务端返回内容到客户端。如下图所示:


网络请求

知道了我们这个库要干什么后,接着我们就要编码了,嗯??等等这就开始写代码了?都不知道要写什么?没错,可能到这你会感觉不知道要怎么写。我们知道Java或者kotlin是一种面向对象的语言,即万物皆对象。所以在编程时要遵从下面的原则:

原则一:面向对象编程

知道了这个原则,当我们遇到一个需求时,第一步就是拆解需求,也就是分析下我们这个需求中都可以分出哪些对象,有了对象的划分,就可以方便的写出对应的class文件。上面图示中我们可以看到,目前我们已经有的对象有client、request、response、还有个server,当然这个server和我们客户端没啥关系,所以这个server就不考虑在内。接着为了方便管理或者使用,我们把request和response都放入一个叫Call的类中,也就是每来一个网络请求,client都会创建一个call对象来对server发起请求。
好接下来我们就来看下这个Call类该如何具体实现,说到具体实现,我们这里还得说下另一个编码原则,就是:

原则二:面向接口编程,而不是面向具体实现编程

什么意思呢,就是我们在写一个类的时候,应该先想想这个类有什么功能,至于这个功能要怎么实现先不用管它。

根据这个原则我们来看看这个Call类中有什么功能,我们上面说了,Call主要功能就是发送request请求到server然后返回响应到client。我们把这个过程定义成一个方法叫做execute,当然我们先不管这个方法怎么实现也就是我们可以先定义一个Call接口,然后里面有一个抽象的execute方法。当我们调用这个execute方法,就会把Call里面的request发送到server,然后返回给我们response。当然这个request我们可以在Call被创建时传入,或者直接在execute中作为参数传入,这里我们就在Call的构造函数中传入。好了我们再想想这个Call还有没有别的功能,有网络的请求自然而然我们就会想到这个请求应该是可以被取消的,这样我们就可以定义一个叫做Cancel的抽象方法用来取消这个请求,同理具体的实现要交给具体的实现类,这里不做实现。当然其实还可以有其他一些功能,只不过这两个是最基本的功能,这样我们先来看下这个Call接口:

interface Call{
    Response execute()
    cancel()
}

好了有Call接口的定义,接下来我们来看下Request类该如何实现,request中的东西就是我们往服务端发送的内容,根据HTTP协议,我们需要知道要向哪个服务器发送数据,也就是需要制定URL,还需要知道我们的请求的请求方法例如post、get或者其他,另外还需要发送HTTP的请求头head和请求体body。这样我们简单归纳下,这个request类中应该含有以下几个属性:url、method、head、body,当然还有一些别的这里暂时不考虑。这样我们的Request类也有了:

class Request{
    HttpUrl url;
    String method;
    Header header;
    RequestBody body;
    public Request(HttpUrl url, String method,Header header,RequestBody body){
        this.url = url;
        this.method = method;
        this.header = header;
        this.body = body;
    }
}

可以看到上面用了HttpUrl、Header、RequestBody几个类对相应的参数进行了再封装,这里就不展开了,这些参数通过构造函数传入。这样看上去没什么问题,但是像这样创建一个类需要比较多的参数时我们可以使用建造者模式来优化,具体什么是建造者模式可以参考我之前写的这篇文章:建造者模式,使用建造者模式后我们来看下request可以怎样创建:

Request request = new Request.Builder()
                            .url(url)
                            .method(method)
                            .header(header)
                            .body(body)
                            .build()

怎么样这样就很清爽吧,同理返回体Response也用建造者模式创建,这里就不展开了。
这样我们创建了Call的接口以及Call中包含的request和response。接下来就可以交给客户端来调用了。

2.API易用性的设计

那么客户端要如何调用呢,这个可以站在我们一个三方库的易用性来考虑,同理就是先不用管这个库具体怎么实现,假如你是一个使用者,你会怎么设计这个库的调用方式。通过这种方式考虑,我们很自然的会想到,首先我们需要先创建一个客户端,然后当我们需要发起一个网络请求时,需要通过这个客户端创建一个具体的Call实例对象,然后这个对象的初始化需要传入一个Request对象。拿到这个Call对象之后,调用它的execute方法来执行具体的网络请求,然后返回请求结果。这样的一个调用流程是非常自然且符合我们的调用习惯的。接下来根据我们的想法来看设计下具体的调用方式:

Request request = new Request.Builder()
                             .url(url)
                             .head()
                             .method()
                             .build()
Client client = new Client()
Call call =  client.newCall(request)
Response response = call.execute()

代码很简单,也很符合我们的调用习惯,没错这个就是官方的调用方式,只不过把Client的名字换成了OkHttpClient。接下来我们通过源码来看下它的具体实现:

3.OkHttp易用性API背后的实现

首先来看下OkHttpClient类,这个类其实结构很简单,采用的是建造者模式,里面很多网络的配置,比如代理、https、dns等配置,一般我们不需要做更改配置采用默认的就好。我们主要看下Call的创建与调用,首先来看下它的创建过程:
点进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 */);
  }

可以看到newCall方法通过一个叫ReaCall类的newRealCall方法来创建,我们再进入这个方法看下:

  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.transmitter = new Transmitter(client, call);
    return call;
  }

可以看到这是一个静态方法,通过这个方法直接创建了一个RealCall对象,然后给他设置了一个transmitter,这个先不用管它,然后就直接返回了,所以接下来我们再看下这个RealCall具体是怎么实例化的:

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
  }

可以看到很简单,就是把传入的参数赋值。这几个参数也很简单,第一个就是我们的客户端client,第二个是请求参数,第三个是这个请求是否是一个websocket请求。这个参数在具体发起请求的时候会用到,这里就先不用管。
到这里我们Call对象就创建好了,接下来我们就来看下最关键的请求执行过程,也就是它的execute方法:
在看execute方法之前,我们先来看下RealCall的大致实现:


final class RealCall implements Call {
  final OkHttpClient client;

  /**
   * There is a cycle between the {@link Call} and {@link Transmitter} that makes this awkward.
   * This is set after immediately after creating the call instance.
   */
  private Transmitter transmitter;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
  }

  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.transmitter = new Transmitter(client, call);
    return call;
  }

  @Override public Request request() {
    return originalRequest;
  }

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();
    transmitter.callStart();
    try {
      client.dispatcher().executed(this);
      return getResponseWithInterceptorChain();
    } finally {
      client.dispatcher().finished(this);
    }
  }

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.callStart();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

  @Override public void cancel() {
    transmitter.cancel();
  }

  @Override public Timeout timeout() {
    return transmitter.timeout();
  }

  @Override public synchronized boolean isExecuted() {
    return executed;
  }

  @Override public boolean isCanceled() {
    return transmitter.isCanceled();
  }

  @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
  @Override public RealCall clone() {
    return RealCall.newRealCall(client, originalRequest, forWebSocket);
  }

  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private volatile AtomicInteger callsPerHost = new AtomicInteger(0);

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    AtomicInteger callsPerHost() {
      return callsPerHost;
    }

    void reuseCallsPerHostFrom(AsyncCall other) {
      this.callsPerHost = other.callsPerHost;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    /**
     * Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        Response response = getResponseWithInterceptorChain();
        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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

  /**
   * Returns a string that describes this call. Doesn't include a full URL as that might contain
   * sensitive information.
   */
  String toLoggableString() {
    return (isCanceled() ? "canceled " : "")
        + (forWebSocket ? "web socket" : "call")
        + " to " + redactedUrl();
  }

  String redactedUrl() {
    return originalRequest.url().redact();
  }

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    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());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }
}

可以看到这个RealCall实现了一个Call接口,我们进入Call看下:

/**
 * 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 {
  /** Returns the original request that initiated this call. */
  Request request();

  /**
   * Invokes the request immediately, and blocks until the response can be processed or is in
   * error.
   *
   * <p>To avoid leaking resources callers should close the {@link Response} which in turn will
   * close the underlying {@link ResponseBody}.
   *
   * <pre>{@code
   *
   *   // ensure the response (and underlying response body) is closed
   *   try (Response response = client.newCall(request).execute()) {
   *     ...
   *   }
   *
   * }</pre>
   *
   * <p>The caller may read the response body with the response's {@link Response#body} method. To
   * avoid leaking resources callers must {@linkplain ResponseBody close the response body} or the
   * Response.
   *
   * <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
   * not necessarily indicate application-layer success: {@code response} may still indicate an
   * unhappy HTTP response code like 404 or 500.
   *
   * @throws IOException if the request could not be executed due to cancellation, a connectivity
   * problem or timeout. Because networks can fail during an exchange, it is possible that the
   * remote server accepted the request before the failure.
   * @throws IllegalStateException when the call has already been executed.
   */
  Response execute() throws IOException;

  /**
   * Schedules the request to be executed at some point in the future.
   *
   * <p>The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually
   * immediately unless there are several other requests currently being executed.
   *
   * <p>This client will later call back {@code responseCallback} with either an HTTP response or a
   * failure exception.
   *
   * @throws IllegalStateException when the call has already been executed.
   */
  void enqueue(Callback responseCallback);

  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
  void cancel();

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
   */
  boolean isExecuted();

  boolean isCanceled();

  /**
   * Returns a timeout that spans the entire call: resolving DNS, connecting, writing the request
   * body, server processing, and reading the response body. If the call requires redirects or
   * retries all must complete within one timeout period.
   *
   * <p>Configure the client's default timeout with {@link OkHttpClient.Builder#callTimeout}.
   */
  Timeout timeout();

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

可以看到,和我们之前定义的差不多,主要有一个execute方法用来执行网络请求,以及还有cancel,获取request,是否超时等方法,这里除了有一个execute网络请求的方法外另外还有一个叫enqueue的方法,这个也是执行网络请求,两者的区别是前者是同步执行,而后者是异步执行。所以这里我们先来看下enqueue这个异步方法的实现过程,知道这个方法的实现,execute自然而然就知道了。

4.OkHttp是如何实现异步执行的?

同理,这里我们先不直接看源码,我们先试着自己实现下。如果是你来实现会怎么想。
首先异常执行我们自然而然想到的是创建一个线程,在子线程中执行。于是我们就想到用以下方式执行:

public enqueue(Callback callback){
    new Thread(new Runnable(){
        Response response = execute()
        callback.success(response)
    }).start()
}

很简单粗暴,就是直接创建一个新的线程,然后在里面执行同步的execute方法,然后等执行结束把结果通过callback回调出去。这用写是可以实现这个异步操作,但是有个问题就是每次要执行网络请求的时候就创建一个新的线程,这样就面临一个问题就是对新创建出来的线程没有起到统一管理的作用。因此我们自然而然想到用线程池来对线程进行统一管理。JavaAPI自带原生的线程池,线程池有如下几个优点:

  • 降低资源消耗
  • 提高响应速度
  • 提高线程的管理性

线程池具体工作方式及实现原理这里不做过多说明,以下是一个简单的工作流程:


Java线程池

改用线程池后,我们上面的代码为:

ThreadPoolExecutor executor = new TreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,timeUnit,workQueue,theadFactory)
executor.execute(runnable)

可以看到很简单创建一个线程池执行器executor,然后调用execute方法并传入runnable对象,就可以执行了,这里的runnable里面执行的就是上面的同步方法execute。
到这里我们就自己大致实现了下这个异步enqueue方法,不过由于有了线程池的引入,就会多了一些线程调度的东西,比如最大连接数等,这样我们可以定义一个调度器Dispatcher用来做统一管理,当然这个类是属于Client中的,起到对Call全局管理作用。

class Dispatcher{
    int maxRequest 
   …
    Executor pools

     public void enqueue(Runnable runnable){
         executor.execute(runnable)
     }
}

大致的实现如上。里面有一个线程池executor,然后有一个enqueue方法,参数为runnable也就是具体执行请求的实现。

好了接下来,我们来看下源码中异步请求是如何实现的,我们先来看下OKHttp的异步调用方式:

var repsonse = client.newCall(request).enqueue(object: Callback{
            override fun onFailure(call: Call, e: IOException) {

            }

            override fun onResponse(call: Call, response: Response) {
            }

        })

它是调用enqueue然后传入一个回调,这个enqueue其实就是调用的RealCall的enqueue方法,我们进入RealCall的enqueue方法看下:


  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.callStart();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

可以看到关键是最后一句代码,调用了客户端的Dispatcher的enqueue方法,并将传入的callback回调封装成了AsyncCall对象传入进enqueue方法,这个AsyncCall稍后再分析,我们先继续看这个Dispatcher的enqueue方法:


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

关键的代码是第一句和最后一句,首先把传入的AsyncCall传入一个readyAsyncCalls队列中,然后最有一句执行promoteAndExecute方法,我们看下这个方法:

  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

这段代码核心其实就是把readyAsyncCalls的AsyncCall对象取出来执行他的executeOn方法,这样我们刚才的异步执行方法enqueue就执行结束了,那么现在就需要来看看这个AsyncCall是什么,以及他的executeOn方法是如何执行的。


  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private volatile AtomicInteger callsPerHost = new AtomicInteger(0);

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    AtomicInteger callsPerHost() {
      return callsPerHost;
    }

    void reuseCallsPerHostFrom(AsyncCall other) {
      this.callsPerHost = other.callsPerHost;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    /**
     * Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        Response response = getResponseWithInterceptorChain();
        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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

可以这个AsyncCall是继承自NamedRunnable类,我们看下NamedRunnable是什么:

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

是不是有内味了,这个NamedRunnable其实就是一个Runnable,它的run方法里执行的是execute方法,而这个方法这里是抽象的,需要子类来实现,它的子类就是我们刚看到的AsyncCall类,所以再回到AsyncCall中看下,首先我们来看下这个类的executeOn方法:

    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

这里从上下文知道executeOn方法传入了一个线程池executorService对象,然后调用线程池executorService的execute方法,线程池的execute方法需要传入runnable对象,这里自己就是继承自runnable,所以直接传入this,这样就会调用我们刚说的execute方法,我们来看下execute方法的实现:

    @Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        Response response = getResponseWithInterceptorChain();
        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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

可以看到这里调用getResponseWithInterceptorChain方法来获取请求的结果然后返回,到这里也就完成了OkHttp的整个异步调用过程。

5.OkHttp中的链式调用

接下来网络执行流程重点就在getResponseWithInterceptorChain方法中了。由于getResponseWithInterceptorChain方法内部采用责任链的设计模式,所以在看这个方法之前我们先来简单看下什么是责任链模式,


责任链模式

如上图所示,在一个公司里,boss从客户那接到一个项目,然后boss把这个项目下发到leader1手中,leader1把任务下发到leader2手中,leader2再把这个项目下发到具体执行的程序员手中,然后程序员完成项目后交付给leader2,leader2再交付给leader1,leader1最后交付给boss。可以看到,这样的层级结构就像是一个链条,我们的任务在这个链条上传递执行,所以这样的工作流程或者模式我们就叫做责任链模式,当然这个项目其实不一定非定到达到程序员手中再上交,其中某个leader也是可以直接拦截掉这个项目然后完成直接上交给自己的上层,所以有时候我们也把链中的每层叫做拦截器,当一个任务传递到你这你可以拦截掉也可以继续向下传递下去。
对责任链模式有了一个大致的印象后,我们再来看下这个getResponseWithInterceptorChain方法:


  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    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());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }

可以看到,这个方法里面其实就是执行一个责任链,首先是定义了一系列拦截器Interceptor,然后把这些拦截器组成一个链条RealInterceptorChain,最后就是执行这个链条,这样我们的一个网络请求就会在每一层拦截器中传递,如下图所示:


okhttp责任链示意图

那么接下来我们的OkHttp分析的重心就放到了各个拦截器的解读。由于篇幅的关系,关于各个拦截器的分析将会放到下一篇文章中介绍。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343