×

OKHttp3 源码解析之整体流程(一)

96
lijiankun24
2017.09.24 23:09* 字数 1508

最近抽时间分析一下 OKHttp3 的源码,关于 OKHttp 源码解析的资料已经有很多了,但是关于 OKHttp3 源码解析的文章似乎不太多,这一系列文章主要是针对 OKHttp3 的源码进行解析。首先,本篇文章将分析一下 OKHttp3 的整体流程,细节相关的知识会在后续的文章中进行介绍。好了,那咱就发车吧~ 本文中涉及到的自定义类的源码都在 Github 上的 OkHttpPractice 工程中。

  1. OKHttp 的简单使用
  2. 请求的整体流程

1. OKHttp 的简单使用

OKHttp 支持异步请求和同步请求,下面简单介绍一下异步请求和同步请求。

1.1 异步请求

因为是异步的网络请求,不会阻塞 UI 主线程,所以异步请求可以直接写在 UI 主线程中。下面是异步的 GET 请求:

       String url = "https://www.baidu.com";
       OkHttpClient client = new OkHttpClient.Builder()
               .build();
       Request request = new Request.Builder()
               .method("GET", null)
               .url(url)
               .build();
       client.newCall(request).enqueue(new Callback() {
           @Override
           public void onFailure(Call call, IOException e) {
               Log.i("lijk", "onFailure " + e.toString());
           }

           @Override
           public void onResponse(Call call, Response response) throws IOException {
               Log.i("lijk", "onResponse ");
           }
       });

1.2 同步请求

相对于异步请求,不可以在 UI 主线程中直接使用异步请求,必须子线程中进行该请求。
下面是同步的 GET 请求:

      try {
          String url = "https://www.baidu.com";
          OkHttpClient client = new OkHttpClient.Builder()
                .build();
          Request request = new Request.Builder()
                .method("GET", null)
                .url(url)
                .build();
          client.newCall(request).execute();
      } catch (IOException e) {
          e.printStackTrace();
      }

2. 请求的整体流程

从上一小节中可以大概知道 OKHttp 简单的使用,我们就从这儿开始分析 OKHttp 请求的整体流程。先分析异步请求,了解异步请求的流程之后,自然会明白同步请求。

2.1 异步请求

2.1.1 OkHttpClient

首先是创建一个 OkHttpClient 对象,可以通过下面两种方式创建 OKHttpClient 的对象,如下所示:

  // 方式一
  OkHttpClient client = new OkHttpClient.Builder()
                    .build();

  // 方式二
  OkHttpClient client = new OkHttpClient();

OKHttpClient 的源码如下所示:

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {

  ......

  public OkHttpClient() {
    this(new Builder());
  }

  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls();
    }

    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;
  }

  ......

  public static final class Builder {
    Dispatcher dispatcher;
    @Nullable Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    EventListener.Factory eventListenerFactory;
    ProxySelector proxySelector;
    CookieJar cookieJar;
    @Nullable Cache cache;
    @Nullable InternalCache internalCache;
    SocketFactory socketFactory;
    @Nullable SSLSocketFactory sslSocketFactory;
    @Nullable CertificateChainCleaner certificateChainCleaner;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;
    boolean followRedirects;
    boolean retryOnConnectionFailure;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    int pingInterval;

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

    ......
  }

  ......

}

可以看到,OKHttpClient 对象是通过 Builder 模式创建的,在 OKHttpClient.Builder() 类的构造方法中,对它的属性都有默认值和默认对象。

2.1.2 RealCall

创建好 OkHttpClient 对象之后,通过调用 OkHttpClient 对象的 newCall(Request request) 方法即可创建一个 Call 对象,newCall(Request request) 方法如下所示:

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

可以看到,newCall(Request request) 方法生成的是 RealCall 类型的对象,RealCall 的源码并不算长,如下所示:


final class RealCall implements Call {
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
  final EventListener eventListener;

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

  // Guarded by this.
  private boolean executed;

  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    final EventListener.Factory eventListenerFactory = client.eventListenerFactory();

    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

    // TODO(jwilson): this is unsafe publication and not threadsafe.
    this.eventListener = eventListenerFactory.create(this);
  }

  ......

}

对于异步请求,会调用 RealCall 对象的 enqueue(Callback responseCallback) 方法,如下所示:

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

enqueue(Callback responseCallback) 方法中,会调用 Dispatcherenqueue(AsyncCall call) 方法,生成一个 AsyncCall 对象,并将其传入到 Dispatcher对象中去。

2.1.3 Dispatcher

接着分析一下 Dispatcher 的源码,如下所示:

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

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

  public Dispatcher() {
  }

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

  ......

  synchronized void enqueue(AsyncCall call) {
    // runningAsyncCalls异步运行任务的队列个数如果小于 64
    // 每个主机同时被请求的个数小于 5
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      // 将异步任务加入到异步运行队列中
      runningAsyncCalls.add(call);
      // 通过线程池运行该异步任务
      executorService().execute(call);
    } else {
      // 如果不满足上面两个条件,则将异步任务加入到预备异步任务队列中
      readyAsyncCalls.add(call);
    }
  }

  ......

}

Dispatcherenqueue(AsyncCall call) 方法中可以看到,得到异步任务之后,如果异步任务运行队列中的个数小于 64 并且每个主机正在运行的异步任务小于 5,则将该异步任务加入到异步运行队列中,并通过线程池执行该异步任务,若不满足以上两个条件,则将该异步任务加入到预备异步任务队列中。

2.1.4 AsyncCall

AsyncCallRealCall 的内部类,源码如下:

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

  @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 {
        // 抛出异常时,回调失败方法
        responseCallback.onFailure(RealCall.this, e);
      }
    } finally {
      // 从 Dispatcher 对象的异步请求队列中移除该任务
      client.dispatcher().finished(this);
    }
  }
}

可以先看一下在 finally 代码块中的部分,调用 Dispatcher 对象的 finished(AsyncCall call) 方法,源码如下:

/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
  finished(runningAsyncCalls, call, true);
}

/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
  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!");
    if (promoteCalls) promoteCalls();
    runningCallsCount = runningCallsCount();
    idleCallback = this.idleCallback;
  }

  if (runningCallsCount == 0 && idleCallback != null) {
    idleCallback.run();
  }
}

private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

最终会调用 finished(Deque<T> calls, T call, boolean promoteCalls) 方法,会将传入的 Call 对象从正在运行的异步任务队列中移除,并将符合条件的预备异步任务队列中的任务加入到正在运行的异步任务队列中,并将其放入线程池中执行。

AsyncCall 中最重要的就是 execute() 方法,其中最重要的就是下面一行:

Response response = getResponseWithInterceptorChain();

可以看到从 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);
    // 通过 RealInterceptorChain 对象链式的调用拦截器,从而得到响应。
    return chain.proceed(originalRequest);
  }

对 OKHttp 有所了解的人应该都知道,拦截器 (interceptor) 是 OKHttp 中非常重要的思想,在源码中拦截器应用的也非常广泛和重要,上面源码中的拦截器串联起来并执行,就完成了本次的网络请求,在下一篇文章中会重点介绍拦截器的知识。

通过对 getResponseWithInterceptorChain() 方法的分析,可以看到将所有的拦截器都聚合之后,生成一个 RealInterceptorChain 拦截器调用链对象 chain,在 chain 中递归的调用所有的拦截器,最后将得到的结果返回。

这里使用的是责任链模式,拦截器分层的思想也是借鉴了网络协议中的分层思想,请求从最上层到最下层,响应是从最下层到最上层。

2.1.5 RealInterceptorChain

public final class RealInterceptorChain implements Interceptor.Chain {

  ......

  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

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

    return response;
  }
}

RealInterceptorChain 中最重要的就是 proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) 方法,将之前的拦截器列表串联起来并执行,得到响应结果之后,再将结果向上一层层返回。

2.2 同步请求

分析完异步请求的流程之后,同步请求的流程就比较容易理解。

和异步请求一样,会先调用 OkHttpClient 中的 newCall(Request request) 方法生成一个 RealCall 对象

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

接下来和异步请求就不一样了,会调用 RealCall 对象的 execute() 方法,源码如下所示:

@Override public Response execute() throws IOException {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  try {
    client.dispatcher().executed(this);
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    client.dispatcher().finished(this);
  }
}
  • 会调用 Dispatcher 对象的 executed(RealCall call) 将该同步任务加入到 Dispatcher 对象的同步任务队列中去
  • 接着会调用 getResponseWithInterceptorChain() 方法获取到请求的响应对象。对,就是之前异步任务请求获取响应对象的同一个方法。
  • 最终,会走 finally 代码块,将此同步任务从 Dispatcher 对象的同步任务队列中移除。

如果理解之前异步任务请求的流程,那么同步请求的流程就非常容易理解了。


本文中涉及到的自定义类的源码都在 Github 上的 OkHttpPractice 工程中。


参考资料:

OkHttp源码解析 -- 俞其荣

OkHttp源码解析 -- 高沛

Android 相关
Web note ad 1