OkHttp 源码解析(一)请求流程分析

OkHttp 源码解析(一)请求流程分析

[TOC]

一、前言

现在大多数 Android 开发者都是使用的 OkHttp 框架进行网络请求的,只不过使用的方式千差万别,有的是在 OkHttp 之上进行封装,有的借助于 Retrofit(OkHttp 的一个封装框架) 又进行了一层封装,封装的方式也各有不同,其实并没有什么好坏之分,适合自己的项目才是最好的。

不过不管使用哪种方式使用 OkHttp,最好是能懂 OkHttp 的原理,理解为什么我们可以很方便的使用它。

本文就是通过基本的同步和异步请求详细解读 OkHttp 原理的。

二、从同步请求流程开始

2.1 一次同步请求

public static void main(String[] args) throws IOException {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(15, TimeUnit.SECONDS)
                .build();

        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        Call call = client.newCall(request);
        Response response = call.execute();
        System.out.println(response.body().string());
}

由于使用了 OkHttp,我们使用上面很简单的代码,实现了一次网路请求,请求结果如下:

image

一个同步的请求过程如下:

  1. 构建 OkHttpClient 对象
  2. 构建 Request 对象
  3. 通过 OkHttpClient 对象和 Request 对象 构建 Call对象
  4. 通过调用 Call对象的 execute() 方法同步获得请求结果 Response。

虽然上面通过 4 步就实现了一次网络请求,实际上,OkHttp 替我们做了很多事情,下面就依次来分析下:

2.2 构建 OkHttpClient 对象

上面我们是通过 Builder 模式构建的 OkHttpClient 对象,我们知道,使用构建者模式,内部必然是有一个 Builder 类的,先看下 OkHttpClient 内的 Builder 类:

public static final class Builder {
    Dispatcher dispatcher;// OkHttp 内部分发类
    Proxy proxy; // 代理
    List<Protocol> protocols;// 应用层协议列表
    List<ConnectionSpec> connectionSpecs;// 指定HTTP流量通过的套接字连接的配置列表
    final List<Interceptor> interceptors = new ArrayList<>(); //用户自定义拦截器集合
    final List<Interceptor> networkInterceptors = new ArrayList<>();// 用户自定义网络拦截器集合
    ProxySelector proxySelector;// 连接时,选择的代理服务器
    CookieJar cookieJar;// 为HTTP Cookie 提供策略和持久性
    Cache cache;// 用户启用缓存时设置的缓存类
    InternalCache internalCache;// 内部缓存
    SocketFactory socketFactory;// Http套接字工厂
    SSLSocketFactory sslSocketFactory;// 使用 Https 时,ssl 套接字工厂类
    CertificateChainCleaner certificateChainCleaner;// 验证确认证书,
    HostnameVerifier hostnameVerifier;// 验证确认Https请求的主机名
    CertificatePinner certificatePinner;//证书锁定,用来约束信任的认证机构。
    Authenticator proxyAuthenticator;// 代理身份验证
    Authenticator authenticator;// 身份验证
    ConnectionPool connectionPool;// 连接池
    Dns dns;// 解析主机名的 ip 地址
    boolean followSslRedirects;// 是否允许 ssl 重定向
    boolean followRedirects;// 是否允许重定向
    boolean retryOnConnectionFailure;// 是否在连接失败时重试
    int connectTimeout;// 连接超时时间
    int readTimeout;// 读取超时时间
    int writeTimeout;// 写入超时时间
    int pingInterval;// ping 的间隔
}

在 构造方法时初始化一些属性如下:

public Builder() {
  dispatcher = new Dispatcher();
  // 协议版本 Protocol.HTTP_2, Protocol.HTTP_1_1
  protocols = DEFAULT_PROTOCOLS;
  //套接字列表ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT
  connectionSpecs = DEFAULT_CONNECTION_SPECS;
  //设置默认代理
  proxySelector = ProxySelector.getDefault();
  // 不使用 Cookie
  cookieJar = CookieJar.NO_COOKIES;
  // 使用默认套接字工厂
  socketFactory = SocketFactory.getDefault();
  hostnameVerifier = OkHostnameVerifier.INSTAN
  certificatePinner = CertificatePinner.DEFAUL
  proxyAuthenticator = Authenticator.NONE;
  authenticator = Authenticator.NONE;
  //创建连接池
  connectionPool = new ConnectionPool();
  dns = Dns.SYSTEM;
  //允许 SSL 重定向
  followSslRedirects = true;
  //允许重定向
  followRedirects = true;
  // 连接失败时会重连
  retryOnConnectionFailure = true;
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;
  pingInterval = 0;
}

上面的 Builder 类的属性基本也是 OkHttpClient 类的一些属性,在使用 Builder 模式时,在创建 Buidler 的时候,会初始化一些配置吗,然后再build 方法中,返回一个 OkHttpClient 对象,

最终构建 OkHttpClient :

public OkHttpClient build() {
      return new OkHttpClient(this);
    }
    
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.proxySelector = builder.proxySelector;
  this.cookieJar = builder.cookieJar;
  this.cache = builder.cache;
  this.internalCache = builder.internalCache;
  this.socketFactory = builder.socketFactory;
  // 默认不是 TLS 的
  boolean isTLS = false;
  // 遍历连接配置列表,看看有没有 TLS 的,前面在 builder 的构造方法里面初始化的三个。
  for (ConnectionSpec spec : connectionSpecs) {
    isTLS = isTLS || spec.isTls();
  }
  // 如果设置了 ssl 套接字工厂,或者不是加密的,就使用默认的,也就是空
  if (builder.sslSocketFactory != null || !isTLS) {
    this.sslSocketFactory = builder.sslSocketFactory;
    this.certificateChainCleaner = builder.certificateChainCleaner;
  // 未设置,并且是 TLS 加密,使用默认的配置
  } 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;
}

2.3 创建 Request

这里的 Request 同样使用的是 Builder 构建者模式,主要存储的信息有 :

public final class Request {
  // 存储了请求 URL相关的信息
  final HttpUrl url;
  // 请求方法,比如 GET、POST 等
  final String method;
  // 请求头类
  final Headers headers;
  //请求体类,我们在 POST 请求的时候,经常会用到,
  final RequestBody body;
  // 标记
  final Object tag;
  // 保证线程可见性的 缓存控制,是延迟初始化的
  private volatile CacheControl cacheControl; // Lazily initialized.
  
  
  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;
}
}

构造方法传入了 Builder 类,

public static class Builder {
  HttpUrl url;
  String method;
  Headers.Builder headers;
  RequestBody body;
  Object tag;
  public Builder() {
    this.method = "GET";
    this.headers = new Headers.Builder();
  }
}

可见,在 Builder 类中,在创建 Builder 的无参构造方法中,默认是使用 “GET”请求的,同时创建了 Headers 类。
重点看下 我们经常用的 url 方法:

public Builder url(String url) {
    // url 不允许为空
  if (url == null) throw new NullPointerException("url == null");
  // Silently replace web socket URLs with HTTP URLs.
  // 替换成 http 或者是 https
  if (url.regionMatches(true, 0, "ws:", 0, 3)) {
    url = "http:" + url.substring(3);
  } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
    url = "https:" + url.substring(4);
  }
  //创建 HttpUrl 对象
  HttpUrl parsed = HttpUrl.parse(url);
  if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
  //调用 url(HttpUrl url)
  return url(parsed);
}
public Builder url(HttpUrl url) {
  if (url == null) throw new NullPointerException("url == null");
  this.url = url;
  return this;
}

通过上面可以看到,我们平时大多数都是传入的 String,Okhttp 内部是会把我们传入的 String 构建成一个 HttpUrl 的对象再去使用的。

HttpUrl 内部包含的常见信息如下:

  • scheme 协议,"http" 或者是 "https"
  • host 请求的主机地址
  • port 段端口号 ,http 是 80 端口,https 是 443 端口
  • pathSegments 地址路径列表

这里不明白的,建议去看一下计算机网络学习之 http 相关中讲解的 URI 和 URL。

通过上面的过程构建出了一个 Request 对象。主要包含的信息其实也就是HTTP 的报文:
请求头、请求体、请求地址、方法什么的都有了,算是对 HTTP 请求报文的封装了。

2.4 构建 Call 对象

Call 是一个接口:

public interface Call extends Cloneable {
  // 返回最初的 Request 对象
  Request request();
  // 同步请求
  Response execute() throws IOException;
  // 异步请求
  void enqueue(Callback responseCallback);
  //如果可能,取消请求,但是完成的请求不能取消
  void cancel();
  // 是否执行
  boolean isExecuted();
  // 是否取消
  boolean isCanceled();
  // copy 一个 Call
  Call clone();
   //接口创建 Call
  interface Factory {
    Call newCall(Request request);
  }
}

Call 对象是根据 OkHttpClient 的 newCall 方法构建的,

@Override public Call newCall(Request request) {
    // 创建 RealCall 对象,可见 RealCall 对象是 Call 的子类
  return new RealCall(this, request, false /* for web socket */);
}

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

  /** 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) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }
}

RealCall 实现了 Call 接口,是 OkHttp中实现具体请求的执行者,里面持有 OkHttpClient 和 Request 对象。

2.5 调用 execute() 完成同步请求

Response response = call.execute();

在 2.4 中讲了,call 其实是 RealCall类的实例,所以调用的是 RealCall 中的 execute() 方法:

@Override public Response execute() throws IOException {
  // 加synchronized 保证线程安全,也就保证了同时只能有一个线程访问,不会发生 executed值的错误
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  // 执行的地方 
  try {
    // 关键点 1  调用分发器类  Dispatcher 的 executed方法,并传入当前 RealCall
    client.dispatcher().executed(this);
    // 关键点 2 使用拦截器链执行网络请求
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    // 关键点 3 销毁本次网络请求
    client.dispatcher().finished(this);
  }
}

这里对关键点 1、2、3 分别讲解:

关键点 1

这里的 Dispatcher 对象是在创建 OkHttpClient 的时候初始化的,如果我们没指定,就会创建一个默认的 Dispatcher 对象(前面创建 OkHttpClient 的时候代码有),具体看下这个非常重要的分发器类:

这个类很重要,所以代码全部贴出来

public final class Dispatcher {
  //最大请求数
  private int maxRequests = 64;
  //一个请求地址的最大请求数
  private int maxRequestsPerHost = 5;
  // 没有请求时的 callback
  private Runnable idleCallback;
  //延迟初始化的线程池,用来执行异步请求的请求
  private ExecutorService executorService;
  /** Ready async calls in the order they'll be run. */
  // 准备运行的双端队列,按照加入顺序运行。
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  // 异步请求的正在运行的双端队列,包含尚未完成的取消请求
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  // 同步请求双端队列,包含尚未完成的取消请求
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

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

  public Dispatcher() {
  }
  // 异步运行时调用,如果未创建线程池,则创建一个。这里使用了 SynchronousQueue,这种队列不会保存元素,在核心线程满的时候,而最大线程数有剩余,直接创建新线程处理任务
  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;
  }

  // 设置同时执行的最大请求书,
  public synchronized void setMaxRequests(int maxRequests) {
    if (maxRequests < 1) {
      throw new IllegalArgumentException("max < 1: " + maxRequests);
    }
    this.maxRequests = maxRequests;
    promoteCalls();
  }
  // 获取最大请求数
  public synchronized int getMaxRequests() {
    return maxRequests;
  }

  //设置一个域名同时最大请求数
  public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
    if (maxRequestsPerHost < 1) {
      throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
    }
    this.maxRequestsPerHost = maxRequestsPerHost;
    promoteCalls();
  }

  public synchronized int getMaxRequestsPerHost() {
    return maxRequestsPerHost;
  }

  //设置每次调度程序空闲时调用的回调(当运行的调用数返回到零时)
  public synchronized void setIdleCallback(Runnable idleCallback) {
    this.idleCallback = idleCallback;
  }
    
  // 异步请求调用  
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

  // 取消所有请求
  public synchronized void cancelAll() {
    for (AsyncCall call : readyAsyncCalls) {
      call.get().cancel();
    }

    for (AsyncCall call : runningAsyncCalls) {
      call.get().cancel();
    }

    for (RealCall call : runningSyncCalls) {
      call.cancel();
    }
  }

  // 主要是异步请求用,从异步准备队列添加到异步运行队列  
  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.
    }
  }

  // 获取传入 call 的同域名的当前正在请求数
  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

  // 同步请求调用
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

  // 异步请求结束时调用
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  // 同步请求结束时调用
  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();
    }
  }

  // 返回当前正在等待的Calls 列表
  public synchronized List<Call> queuedCalls() {
    List<Call> result = new ArrayList<>();
    for (AsyncCall asyncCall : readyAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }

  // 返回当前正在运行的 Calls 列表
  public synchronized List<Call> runningCalls() {
    List<Call> result = new ArrayList<>();
    result.addAll(runningSyncCalls);
    for (AsyncCall asyncCall : runningAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }
  // 返回当前正在等待的 Calls 大小
  public synchronized int queuedCallsCount() {
    return readyAsyncCalls.size();
  }
  // 返回当前正在运行的 Calls 大小
  public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }
}

Dispatch 的代码不是很多,但却是非常重要的一个类,用来进行具体的网络请求分发。

现在继续看关键点 1,这里执行了 Dispatch 的 executed 方法 。

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

很简单 ,直接把 Call 对象添加到同步请求队列中,别的也没做什么操作。

关键点 2
Response result = getResponseWithInterceptorChain();

这里就是 OkHttp 实现网络请求最核心的地方,通过拦截器链实现请求。

Response getResponseWithInterceptorChain() throws IOException {
  // 构建一整套连接器
  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));
  //如果不是 web 连接,添加用户自定义网络拦截器
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors());
  }
  //添加请求服务拦截器
  interceptors.add(new CallServerInterceptor(forWebSocket));
  // 构建拦截器链
  Interceptor.Chain chain = new RealInterceptorChain(
      interceptors, null, null, null, 0, originalRequest);
  // 执行拦截器链    
  return chain.proceed(originalRequest);
}

可以看到这里是通过拦截器链请求,并其中返回结果的。

其中 RealInterceptorChain 实现了 Interceptor.Chain 接口,代码如下

public final class RealInterceptorChain implements Interceptor.Chain {
  // 拦截器链
  private final List<Interceptor> interceptors;
  //
  private final StreamAllocation streamAllocation;
  // 对 Http 请求进行编码,对 Http响应进行解码
  private final HttpCodec httpCodec;
  // 连接
  private final Connection connection;
  // 当前索引
  private final int index;
  // 请求
  private final Request request;
  // 计数器,保证不能多次执行,多次执行会异常
  private int calls;

  // 构造函数
  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, Connection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }
  // 省略部分代码。。。
  
  // 这里是核心的 proceed 方法,根据传入的 request 以及构造时生成的信息,调用另外一个 proceed 方法
  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

  // 具体处理 
  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
    // 如果是当前的索引,大于总拦截器个数,抛出异常  
    if (index >= interceptors.size()) throw new AssertionError();
    // 没超过,就执行次数+1
    calls++;

    //确保一个拦截器链中的请求都是执行同个地址的
    if (this.httpCodec != null && !sameConnection(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // 确保一个拦截器链中的每个拦截器只能执行一次
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // 生成一个新的 RealInterceptorChain 对象,准备调用拦截器链中的下一个拦截器
    // 注意这是是使用的 index+1 依次进行构建的,一直到 CallServerInterceptor,
    // CallServerInterceptor中不会再调用
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    // 拿到当前的拦截器
    Interceptor interceptor = interceptors.get(index);
    // 使用当前拦截器的 intercept 调用下个拦截器
    Response response = interceptor.intercept(next);

    // 确认下个拦截器对 chain.proceed() 进行了调用
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // 确认返回值不为 null
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }
    // 返回响应结果 Response 对象
    return response;
  }

  // 判断请求地址是够相同  
  private boolean sameConnection(HttpUrl url) {
    return url.host().equals(connection.route().address().url().host())
        && url.port() == connection.route().address().url().port();
  }
}

上面对 RealInterceptorChain 进行了大概的介绍,RealInterceptorChain 的原理其实就是在其内部会依次构建新拦截器链并用当前拦截器调用下个拦截器。

流程如下:

1、在 RealCall 中会先生成第一个 RealInterceptorChain 对象,执行其 proceed 方法
// 这里传入index 传入 0,代表 List 中第一个拦截器,并且把原始请求传入
Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);

注意这里传入了三个 null,对应的分别是 StreamAllocation streamAllocation, HttpCodec httpCodec, Connection connection,

2、在 RealInterceptorChain 内部的 proceed 方法中执行拦截器
image

在这里,依次调用下个拦截器的 intercept 方法去获取 Response 结果,这里先以 RetryAndFollowUpInterceptor 的 intercept 方法为例,看看做了什么 :

3、在拦截器内部的 intercept 方法中,执行传入的 Chain 的 proceed 方法,构成闭环

RetryAndFollowUpInterceptor 的 intercept 方法:


image

chain 是我们传入的下个拦截器链,可以看到在 RetryAndFollowUpInterceptor 的 Intercept内部,会调用传入的 chain 的 proceed 方法去获取结果,这就又回到了上面的的 RealInterceptorChain 的 proceed 方法里面,依次又调用下个拦截器去获取结果。一直到最后一个 Intercept 方法调用结束,然后会依次向前返回 Response 结果。

4、在CallServerIntercept 内部返回结果

在最后的一个拦截器 CallServerIntercept 中,不会再去调用 传入的 Chain 对象,完成最终的数据交互,然后依次返还给上一级。

大致流程如下(先不考虑缓存等情况):


image

是不是很棒的设计,使用了责任链模式很棒的完成了一次网络请求的处理。

关于每个拦截器的具体实现,后面的文章再去分析。

关键点 3
client.dispatcher().finished(this);

这行代码是在 finally 代码块里面的,意味着不管是请求成功或者失败,都会执行到。

这里调用了 Dispatcher 对象里面的 finished 方法,并传入了当前 Call 对象,其实这个方法前面再讲 Dispatcher 的时候已经讲过,再来看下

void finished(RealCall call) {
  // 调用重载finished方法, 由于是同步请求,传入同步请求队列,然后传入当前 Call 对象,
  //第三个参数是是否调用  promoteCalls() 函数控制的,promoteCalls() 是只在 异步请求的时候调用,所以这里传入 false
  finished(runningSyncCalls, call, false);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
  int runningCallsCount;
  Runnable idleCallback;
  // 线程同步
  synchronized (this) {
    // 从当前同步队列移除 call,如果失败,抛出异常
    if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
    // 异步请求调用
    if (promoteCalls) promoteCalls();
    // 统计正在运行的同步和异步请求的个数
    runningCallsCount = runningCallsCount();
    idleCallback = this.idleCallback;
  }
  // 同步和异步请求的个数为 0,也就是没有请求了,并且idleCallback 不为空,则运行idleCallback 处理一些事情
  if (runningCallsCount == 0 && idleCallback != null) {
    idleCallback.run();
  }
}

public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }

其实上面的也就是把完成的网络请求进行移除,然后如果是全部网络请求完成,则可以通过idleCallback.run 执行一些操作。

通过以上就是一个完整的同步请求,从创建、请求、响应、完成、销毁的过程,下面再来看下异步请求。

三、异步请求分析

3.1 一次异步请求

image

一个异步的请求过程如下:

  1. 构建 OkHttpClient 对象
  2. 构建 Request 对象
  3. 通过 OkHttpClient 对象和 Request 对象 构建 Call对象
  4. 通过调用 Call对象的 enqueue() 方法异步获得请求结果 Response。

这其中的前三步,和同步请求时一样的,不一样的就是第四步,具体来看下第四步:

3.2 构建 OkHttpClient 对象

参考同步请求

3.3 构建 Request 对象

参考同步请求

3.4 通过 OkHttpClient 对象和 Request 对象 构建 Call对象

参考同步请求

3.5 通过调用 Call对象的 enqueue() 方法异步获得请求结果 Response。

RealCall 的 enqueue 方法如下:

@Override public void enqueue(Callback responseCallback) {
  // 线程同步,保证状态安全
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  // 构建一个 AsyncCall 对象,并调用 Dispatcher 的 enqueue 方法:
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

先看 Dispatcher 的 enqueue 方法, AsyncCall 暂且先放下:

synchronized void enqueue(AsyncCall call) {
  // 当前请求数小于最大请求数,并且一个服务器的连接数小于最大同服务器连接数 
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    //符合条件就直接加入运行队列,
    runningAsyncCalls.add(call);
    // 然后通过线程池执行
    executorService().execute(call);
  } else {
    // 如果超过了最大限制,就添加到准备队列中
    readyAsyncCalls.add(call);
  }
}

// 获得一个线程池
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;
}

这里很明显,使用了线程池的 execute方法,也就是说这个 call(AsyncCall) 是一个Runnable 对象。

final class AsyncCall extends NamedRunnable {}

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

果不其然,AsyncCall 继承于 NamedRunnable,是一个 Runnable 对象,在 NamedRunnable 内部实现了 run 方法,并且在 run 内部调用抽象函数 execute() ,AsyncCall 只用实现 execute 方法就好了:

final class AsyncCall extends NamedRunnable {
  private final Callback responseCallback;
  AsyncCall(Callback responseCallback) {
    super("OkHttp %s", redactedUrl());
    this.responseCallback = responseCallback;
  }
  @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 {
      // 最终也会移除完成的网络请求  
      client.dispatcher().finished(this);
    }
  }
}

可以看到,这里和同步请求很类似,都是通过 getResponseWithInterceptorChain() 去完成网络请求的,所以说拦截器链是 OkHttp 的核心,最终请求完成以后从正在运行队列中移除请求。一个异步请求就完成了。

那么这里有个问题,前面我们讲了,在 Dispatcher 分发器类中,关于异步请求的有队列,一个异步准备队列、一个异步正在执行队列,上面讲的是正在执行队列的过程,那么准备队列里面什么时候能够得到执行呢?

还是要回到 finished 方法

client.dispatcher().finished(this);

  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
  
    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();
    }
  }

这里和同步请你去不同的是promoteCalls 传入了 true,那么就会执行:

if (promoteCalls) promoteCalls();

  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 
      AsyncCall call = i.next();

      // 如果同个服务器地址正在运行的请求数  小于同个最大服务器地址请求数
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        // 从准备队列移除
        i.remove();
        //添加到运行队列
        runningAsyncCalls.add(call);
        // 执行请求
        executorService().execute(call);
      }
      // 如果正在运行数大于最大请求数,结束循环
      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

到这里,一个完整的一步请求过程就算讲完了。

四、最后

这是 OkHttp 源码分析的第一篇,主要讲请求的流程,关于具体拦截器分析,请关注后续。

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

推荐阅读更多精彩内容