OkHttp源码分析

关于OkHttp的使用可以看这篇文章https://blog.csdn.net/Icarus_/article/details/103702167
OkHttp的源码地址https://github.com/square/okhttp,我这里看的是3.10.0版本

1.OkHttpClient的创建

创建方式有2种,一种是通过建造者模式建造出来,一种是直接new出来,直接new一个OkHttpClient对象内部也是通过建造者模式完成初始化。

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

看一下OkHttpClient有哪些参数

OkHttpClient(Builder builder) {
    //分发器dispatcher对象,记录请求执行情况,内部维护一个线程池执行异步请求
    this.dispatcher = builder.dispatcher;
    //配置代理类型(直连、HTTP代理、Socket代理),以及socket地址
    this.proxy = builder.proxy;
    //协议及和,默认协议包含HTTP2和HTTP1.1
    this.protocols = builder.protocols;
    //连接规范,指定socket连接的配置
    this.connectionSpecs = builder.connectionSpecs;
    //应用拦截器集合
    this.interceptors = Util.immutableList(builder.interceptors);
    //网络拦截器集合
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    //事件监听工厂
    this.eventListenerFactory = builder.eventListenerFactory;
    //代理选择器
    this.proxySelector = builder.proxySelector;
    //为HTTP的cookies提供策略和持久化
    this.cookieJar = builder.cookieJar;
    //磁盘缓存,内部使用DiskLruCache
    this.cache = builder.cache;
    //OkHttp内部缓存
    this.internalCache = builder.internalCache;
    //socket工厂类
    this.socketFactory = builder.socketFactory;
    //https相关成员变量初始化
    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.connectionPool = builder.connectionPool;
    //DNS
    this.dns = builder.dns;
    //是否跟随SSL重定向
    this.followSslRedirects = builder.followSslRedirects;
    //是否跟随重定向
    this.followRedirects = builder.followRedirects;
    //连接失败是否重试
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    //连接是否超时
    this.connectTimeout = builder.connectTimeout;
    //连接超时时间
    this.readTimeout = builder.readTimeout;
    //读超时时间
    this.writeTimeout = builder.writeTimeout;
    //ping间隔时长
    this.pingInterval = builder.pingInterval;

    ......
}

2.Call的创建

创建好OkHttpClient后,完成了许多成员变量的初始化,接下来是使用OkHttpClient对象创建一个Call对象。Call对象可以当作对请求request的再次封装,表示该请求已经准备好可以随时发送。此外,一个Call可以取消,但是不允许执行2次。Call的创建如下

okHttpClient.newCall(request);

//返回一个RealCall对象
@Override public Call newCall(Request request) {
  //第一个参数,okHttpClient对象
  //第二个参数,将要发送的请求
  //第三个参数,是否使用websocket
  return RealCall.newRealCall(this, request, false /* for web socket */);
}

3.OkHttp的任务调度

  • 发送的同步/异步请求都会在dispatcher管理其状态
  • dispatcher的作用是维护请求的状态,并维护一个线程池,用于执行请求

看一下dispatcher的源码中很重要的3个变量

//线程池
private @Nullable ExecutorService executorService;
//正在执行的异步请求,包含了已经取消但没有执行完的异步请求
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//就绪状态的请求队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

为什么异步请求需要2个队列呢,可以将dispatcher理解为生产者消费者模型,所以需要2个队列来存放正在执行的和等待的异步请求

4.同步请求

通过newCall方法返回的实际上是RealCall对象,如果是同步请求,调用RealCall对象的execute方法执行请求

okHttpClient.newCall(request).execute();

@Override public Response execute() throws IOException {
  synchronized (this) {
    //如果已经执行过了,抛出异常
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  eventListener.callStart(this);
  try {
    //获取OkHttpClient对象中的dispatcher对象
    //调用dispatcher对象的executed方法标记请求已经发送
    client.dispatcher().executed(this);
    //通过拦截器获取网络响应
    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);
  }
}

看一下dispatcher().executed,每次有新的请求到来时候直接加入到运行队列中

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

5.异步请求

异步请求调用的是RealCall对象的enqueue方法,传入一个回调接口Callback对象,内部调用dispatcher对象的enqueue方法传入,传入一个AsyncCall对象,AsyncCall作为一个Runnable

okHttpClient.newCall(request).enqueue();

@Override public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  eventListener.callStart(this);
  //调用dispatcher对象的enqueue方法传入,传入一个AsyncCall对象
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

而在dispatcher对象的enqueue方法中,会使用线程池执行AsyncCall这个Runnable

synchronized void enqueue(AsyncCall call) {
  //检查请求是否超过最大请求数,和一个HOST对应的最大请求数
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    //添加到正在请求队列中,标记异步请求正在执行
    runningAsyncCalls.add(call);
    //使用线程池执行AsyncCall
    executorService().execute(call);
  } else {
    //将请求加入到就绪异步等待队列,缓存等待
    readyAsyncCalls.add(call);
  }
}

这个ExecutorService的创建和一般的ThreadPoolExecutor的创建都是类似的,但是注意下前三个参数

  • 1.核心线程池数量为0,如果空闲一段时间之后就会将所有线程全部销毁
  • 2.最大的线程数设置为最大值,当下一个请求过来的时候可以无限扩充最大值
  • 3.线程数大于核心线程数时候,多余的线程最大存活时间60秒
    这样设置的目的,比如项目中同时开启20个并发的请求,线程池也会创建20个线程,当工作完毕后60秒就会关闭所有无用的线程。
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;
}

接下来就要看一下AsyncCall是怎么执行的了,AsyncCall是Dispatcher的内部类,继承自NamedRunnable,NamedRunnable会保存自己执行线程的线程名,在其run方法中调用了一个抽象方法execute,子类实现execute方法完成任务。看一下这个Runnable

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

AsyncCall中也是调用核心代码getResponseWithInterceptorChain获取网络响应结果,然后回调Callback。AsyncCall的实现如下

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) {
      .......
    } finally {
      //请求完之后finish
      client.dispatcher().finished(this);
    }
  }
}

看一下这个dispatcher().finished方法,

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

5.拦截器链的构建

拦截器是okHttp中一个强大的机制,可以实现网络监听,请求,响应重写,失败重试等。
同步和异步请求都调用了getResponseWithInterceptorChain()方法,方法中创建了一个拦截器集合,先后顺序依次是:应用拦截器,retryAndFollowUpInterceptor,BridgeInterceptor,CacheInterceptor,ConnectInterceptor,网络拦截器,CallServerInterceptor。

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));
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors());
  }
  interceptors.add(new CallServerInterceptor(forWebSocket));
  //创建RealInterceptorChain
  //第一个参数是拦截器集合
  //第二个参数是指向拦截器的下标,0表示拦截器集合中的第一个
  Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
      originalRequest, this, eventListener, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());
  //启动拦截器链
  return chain.proceed(originalRequest);
}

拦截器是OkHttp中最精巧的设计,遵循单一职责原则,每个拦截器都只针对网络请求或者网络响应做一项处理,拦截器之间使用RealInterceptorChain连接,整个拦截器构成了处理网络请求和响应的流水线。原始请求作为材料,经过拦截器的层层加工,然后发送出去,返回的网络请求也经过拦截器层层处理,最终返回给应用层。

6.拦截器的启动

在getResponseWithInterceptorChain方法中,创建了拦截器集合,初始化第一个RealInterceptorChain对象,将他指向拦截器集合的第一个拦截器,就是把index赋值为0,然后调用RealInterceptorChain对象的proceed方法启动拦截器链

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
  ......

  //创建一个新的RealInterceptorChain
  //第五个参数index + 1表示下一个拦截器的下标
  RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
      connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
      writeTimeout);
  //通过下标获取当前要执行的拦截器
  Interceptor interceptor = interceptors.get(index);
  //当前拦截器处理完后,要执行下一个拦截器,则调用传入的next的proceed方法
  Response response = interceptor.intercept(next);
  ......
  return response;
}

在proceed方法中,通过下标index获取到当前应该执行的拦截器,然后调用该拦截器的intercept方法,并传入一个新建的RealInterceptorChain对象的next,next指向下一个拦截器。所以一般情况下,如果一个拦截器处理完成之后,想要执行下一个拦截器,则可以在拦截器的intercept方法中调用next.proceed方法,拦截器集合中的拦截器就能一个个按顺序执行

7.拦截器总结

1.创建一系列拦截器,放入拦截器集合中
2.创建一个拦截器链RealInterceptorChain,执行拦截器链的proceed方法
3.发起请求前对request进行处理
4.调用下一个拦截器获取response
5.对response进行处理,返回给上一个拦截器

8.RetryAndFollowUpInterceptor

拦截器链启动后,拦截器依次执行,首先执行的是应用拦截器,如果没有设置应用拦截器,则执行RetryAndFollowUpInterceptor,他处理错误恢复和重定向。如果在执行请求过程中出现异常,他会判断如果满足重试条件就发送重试失败的请求,如果网络响应中包含重定向信息,他就会创建重定向请求重新发送,他的intercept方法如下

@Override public Response intercept(Chain chain) throws IOException {
  Request request = chain.request();
  ......
  //用来建立执行http请求所需要的网络组件,他在这个拦截器中并没有被使用到,
  //会在ConnectInterceptor使用,主要用于获取连接服务端的connection,和用于服务端之间数据传输的输入输出流
  StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
  ......
  while (true) {
    ......
    Response response;
    boolean releaseConnection = true;
    try {
      //执行下一个拦截器获取响应
      response = realChain.proceed(request, streamAllocation, null, null);
      releaseConnection = false;
    } catch (RouteException e) {
      // The attempt to connect via a route failed. The request will not have been sent.
      if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
        throw e.getLastConnectException();
      }
      releaseConnection = false;
      //如果满足条件则重试
      continue;
    } catch (IOException e) {
      // An attempt to communicate with a server failed. The request may have been sent.
      boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
      if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
      releaseConnection = false;
      //如果满足条件则重试
      continue;
    } finally {
      // We're throwing an unchecked exception. Release any resources.
      if (releaseConnection) {
        streamAllocation.streamFailed(null);
        streamAllocation.release();
      }
    }

    // Attach the prior response if it exists. Such responses never have a body.
    if (priorResponse != null) {
      response = response.newBuilder()
          .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
          .build();
    }
    //获取重定向请求
    Request followUp = followUpRequest(response, streamAllocation.route());
    //如果重定向请求为空
    if (followUp == null) {
      if (!forWebSocket) {
        streamAllocation.release();
      }
      //不需要重定向,直接返回
      return response;
    }
    //对重试次数进行判断
    if (++followUpCount > MAX_FOLLOW_UPS) {
      streamAllocation.release();
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }
    ......
    //检查是否可以执行重定向,不满足会抛出异常
    ......
    //将请求重新赋值为重定向的请求,继续while循环,再次发送
    request = followUp;
    priorResponse = response;
  }
}

总结一下:
1.创建StreamAllocation对象
2.调用RealInterceptorChain.proceed进行网络请求
3.根据异常结果和响应结果判断是否需要重新请求
4.调用下一个拦截器,对response进行处理,返回给上一个拦截器

9.BridgeInterceptor

在RetryAndFollowUpInterceptor的intercept方法中,调用RealInterceptChain对象的proceed方法,由于RealInterceptChain的下标已经指向了RetryAndFollowUpInterceptor后面的拦截器BridgeInterceptor,所以接下来执行BridgeInterceptor的intercept方法。BridgeInterceptor是桥接器,桥接应用层和网络层的代码,这里会将用户传递过来的请求进行加工,使之成为真正的网络请求,也会对网络响应做响应的处理。如果用户在请求头中没有配置Accenting-Encoding字段,则会将其设置成gzip,服务器会返回gzip压缩的网络响应数据,相应地客户端也会对网络响应做压缩处理,他的intercept方法如下

@Override public Response intercept(Chain chain) throws IOException {
  //获取用户请求
  Request userRequest = chain.request();
  //真正发送网络请求发构建者
  Request.Builder requestBuilder = userRequest.newBuilder();

  RequestBody body = userRequest.body();
  //设置一些请求头
  //Content-Type,Content-Length,Transfer-Encoding,Host,Connection,
  //Accept-Encoding,Cookie,User-Agent
  ......
  //执行下一个拦截器,发送请求,获取网络响应
  Response networkResponse = chain.proceed(requestBuilder.build());
  //将网络请求,将服务器返回的response转化为用户可以使用的response
  HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

  Response.Builder responseBuilder = networkResponse.newBuilder()
      .request(userRequest);
  //在请求头中没有配置Accenting-Encoding字段,则会将其设置成gzip,
  //服务器会返回gzip压缩的网络响应数据,相应地客户端也会对网络响应做压缩处理
  if (transparentGzip
      && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
      && HttpHeaders.hasBody(networkResponse)) {
    //GzipSource用于解压缩
    GzipSource responseBody = new GzipSource(networkResponse.body().source());
    //返回应用层之前去掉不需要的头
    Headers strippedHeaders = networkResponse.headers().newBuilder()
        .removeAll("Content-Encoding")
        .removeAll("Content-Length")
        .build();
    responseBuilder.headers(strippedHeaders);
    String contentType = networkResponse.header("Content-Type");
    responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
  }

  return responseBuilder.build();
}

总结一下:
1.将用户构建好的request请求转化为能够进行网络访问的请求
2.将符合网络请求的request进行网络请求
3.将服务器返回的response转化为用户可以使用的response

10.CacheInterceptor

BridgeInterceptor之后的下一个拦截器是CacheInterceptor,他用于缓存的查找与保存,网络请求到来后,先查找有没有这个请求对应的缓存,如果有则直接返回缓存,没有则调用后面的拦截器发送网络请求,并将获取到的响应缓存起来。他的Cache是通过DiskLruCache算法实现的
CacheInterceptor的intercept方法实现如下

@Override public Response intercept(Chain chain) throws IOException {
  Response cacheCandidate = cache != null
      ? cache.get(chain.request())
      : null;

  long now = System.currentTimeMillis();
  //检查缓存策略
  CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
  //如果还是要发送网络请求则networkRequest和cacheResponse不为null
  Request networkRequest = strategy.networkRequest;
  Response cacheResponse = strategy.cacheResponse;
  ......
  
  // If we don't need the network, we're done.
  //如果缓存可用,则不发送网络请求,直接返回
  if (networkRequest == null) {
    return cacheResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .build();
  }

  Response networkResponse = null;
  try {
    //如果缓存不可用,则执行下一个拦截器,获取网络响应
    networkResponse = chain.proceed(networkRequest);
  } finally {
    // If we're crashing on I/O or otherwise, don't leak the cache body.
    if (networkResponse == null && cacheCandidate != null) {
      closeQuietly(cacheCandidate.body());
    }
  }

  // If we have a cache response too, then we're doing a conditional get.
  if (cacheResponse != null) {
    if (networkResponse.code() == HTTP_NOT_MODIFIED) {
      Response response = cacheResponse.newBuilder()
          .headers(combine(cacheResponse.headers(), networkResponse.headers()))
          .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
          .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
          .cacheResponse(stripBody(cacheResponse))
          .networkResponse(stripBody(networkResponse))
          .build();
      networkResponse.body().close();

      // Update the cache after combining headers but before stripping the
      // Content-Encoding header (as performed by initContentStream()).
      cache.trackConditionalCacheHit();
      cache.update(cacheResponse, response);
      return response;
    } else {
      closeQuietly(cacheResponse.body());
    }
  }
  //包装网络响应
  Response response = networkResponse.newBuilder()
      .cacheResponse(stripBody(cacheResponse))
      .networkResponse(stripBody(networkResponse))
      .build();
  //如果用户配置了缓存
  if (cache != null) {
    //是否满足缓存条件
    if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
      // Offer this request to the cache.
      CacheRequest cacheRequest = cache.put(response);
      //将响应写入缓存并返回
      return cacheWritingResponse(cacheRequest, response);
    }
    //判断是否是无效的缓存方法
    if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          //如果是,要在缓存池中删除request
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
  }

  return response;
}

11.ConnectInterceptor

ConnectInterceptor主要是给网络请求提供一个连接,然后执行下一个拦截器,看他的intercept方法

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Request request = realChain.request();
  StreamAllocation streamAllocation = realChain.streamAllocation();

  // We need the network to satisfy this request. Possibly for validating a conditional GET.
  boolean doExtensiveHealthChecks = !request.method().equals("GET");
  //获取一个HTTP编码解码器,为Http1Codec或者Http2Codec
  HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
  //RealConnection是做实际的网络传输的,获取一个连接
  RealConnection connection = streamAllocation.connection();
  //执行下一个拦截器
  return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

总结:
1.ConnectInterceptor获取Interceptor传过来的streamAllocation
2.将刚才创建的用于网络IO的RealConnection对象,以及和服务器交互的最为关键的HttpCodec等对象传递给后面的拦截器

不管是http1.0还是2.0的keepAlive还是多路复用机制都需要引入连接池的概念,来维护整个okHttp的网络连接,okhttp会将客户端和服务端抽象成connection类,RealConnection就是他的实现类,为了管理所有的RealConnection,提供了ConnectionPoll这个类,他就是为了管理这些链接复用的,当他们共享相同的地址,就可以复用链接

12.CallServerInterceptor

如果设置了网络拦截器,那么ConnectInterceptor执行完就会执行网络拦截器,如果没设置,则执行CallServerInterceptor,他是okhttp最后一个拦截器,网络请求最终从这里发出去,他的intercept方法如下

@Override public Response intercept(Chain chain) throws IOException {
  ......
  //使用ConnectInterceptor中获取到的httpCodec发送网络请求
  httpCodec.finishRequest();
  .......
  //构建一个网络响应对象
  Response response = responseBuilder
      .request(request)
      .handshake(streamAllocation.connection().handshake())
      .sentRequestAtMillis(sentRequestMillis)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build();

  ......
  
  if (forWebSocket && code == 101) {
    // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
    response = response.newBuilder()
        .body(Util.EMPTY_RESPONSE)
        .build();
  } else {
    //写入响应体
    response = response.newBuilder()
        .body(httpCodec.openResponseBody(response))
        .build();
  }

  ......
  //返回网络响应结果
  return response;
}

你可能感兴趣的:(Android)