有关OKHttp3的原理(源码浅析)

简介

OKhttp3 是一个支持 HTTP 和 HTTP2 的 java 或者 Android 网络请求SDK.

也就是说通知支持http 和 http2 网络协议,适用于 java 环境,因为android 也是java 编写的,所有 它可以用于android 中网络请求。

Okhttp3 依赖OKIO,okio 是一个高效的IO读写流(相对于我们java的 IO 流), java 版本的okio 是1.x。 Kotlin 版本的 okio 是2.x

官方文档:OKhttp3

环境要求

目前最新版本的Okhttp3 版本为 3.14.2。 最新版本的okhttp3 要求Android 手机版本为Android5.0+ (API 版本大于等于21),Java 版本必须是Java8 以上,官方强烈建议我们使用最新版本的okhttp3. 因为客户端采用与最新版本的okhttp3 保持同步是抵御https 网络安全问题的重要措施和手段。

注意:目前我们大多数android app 都是支持 android 5.0 一下,并且大多数都是用java 7写的,所以用最新版的okhttp3 显然是不支持的。

Okhttp3 3.12.x 支持 Android 2.3+(API 9+),和 java7。 但是这个版本使用的是TSL1.2 , TSL(Transport Layer Security Protocol,传输层安全协议。 3.12.x 版本后 okhttp3 支持 TLS1.3TSL1.3 相对于 TSL1.2 来说速度更快,安全性更高。所以okhttp 官方不太建议我们使用最低版本的okhttp3,虽然不建议,但是也没办法,目前大多数应用都会支持Android5.0 以下的设备,最新版本的okhttp3 又不支持,所以我们不得不使用 3.12.x 版本的okhttp3, 所以okhttp3 开发人员也知道这个情况,但是由于在3.12.x 版本中去升级TSL1.3 对他们来说也是一件不容易的事情,因此他们在2020年12月31日前在 3.12.x 版本中提供该修复

集成

依赖

////如果需要支持API21 或者 java7 需改为3.12.x, 比如3.12.0
implementation"com.squareup.okhttp3:okhttp:3.14.2")
​
//log 日志拦截器,方便我们查看网络请求的各种信息,版本号改为和 okhttp3 一样
implementation "com.squareup.okhttp3:logging-interceptor:3.14.2" 

混淆

# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
​
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
​
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
​
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform

OKhttp3 核心

Okhttp3 是一个高性能网络请求框架,支持支持HTTPS/HTTP2/WebSocket ,其核心有一下几点:

  1. 采用责任链方式的拦截器,实现分层处理网络请求,让用户对网络请求实现了更好的扩展

  2. GZIP处理降低了下载数据的大小

  3. 支持http 缓存

  4. 采用线程池(thread pool)和连接池(socket pool)解决多并发问题,同时连接池采支持多路复用(http2才支持多路复用,单路复用指的是,一个socket 只有把一个请求发送完成后,才能继续发送第二个请求,多路复用可以让一个socket 同时发送多个请求的数据,内部会自动维护顺序),减少连接创建开销

  5. 底层采用 socket 和服务器进行连接。

  6. 采用okio 实现高效的io流读写

Okhttp3 支持同步和异步两种方式请求:

 //同步请求
 OkHttpClient client = new OkHttpClient();
 // 或者
 OkHttpClient client = new OkHttpClient.Builder().url("").build();
​
 Request request = new Request.Builder()
 .url("http://myproject.com/helloworld.txt")
 .build();
 Response response = client.newCall(request).execute(); // 同步

 //异步请求
 OkHttpClient client = new OkHttpClient();
 Request request = new Request.Builder()
 .url("http://myproject.com/helloworld.txt")
 .build();
 client.newCall(request).enqueue(new Callback() { // 异步
 @Override
 public void onFailure(Call call, IOException e) {
 Log.d("OkHttp", "Call Failed:" + e.getMessage());
 }
 @Override
 public void onResponse(Call call, Response response) throws IOException {
 Log.d("OkHttp", "Call succeeded:" + response.message());
 }

拦截器

对于Okhttp的使用,不能仅限于“会”用,而是要了解其原理。下图为OKhttp 拦截器流程示意图

1105320-20180403182754939-2128769288.png

可以看出,Interceptor贯穿了整个请求过程,是在请求执行过程中扮演重要角色。

这是okhttp的请求执行过程,从应用发出request,到应用收到response,期间经历了N个拦截器。

拦截器的作用

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.

拦截器是一种强大的机制,可以监视,重写和重试网络请求。

下面是一个简单的拦截器,记录传出请求和传入响应:

class LoggingInterceptor implements Interceptor {
 @Override 
 public Response intercept(Interceptor.Chain chain) throws IOException {
 //第一步,获得chain内的request,这个Request,就是我自己new 的 Request 对象
 Request request = chain.request();
​
 long t1 = System.nanoTime(); // 获取发送请求是时间

 logger.info(String.format("Sending request %s on %s%n%s",
 request.url(), chain.connection(), request.headers()));
 //第二步,用chain执行request
 Response response = chain.proceed(request);
​
 long t2 = System.nanoTime();
 logger.info(String.format("Received response for %s in %.1fms%n%s",
 response.request().url(), (t2 - t1) / 1e6d, response.headers()));
 //第三步,返回response
 return response;
 }
}
​
// 添加一个自定义的拦截器
 OkHttpClient httpClient = new OkHttpClient.Builder()
 .addInterceptor(new LoggingInterceptor())

对照最上方的Okhttp工作流程图,可以观察到,在OkhttpCore即把response返回给application时,拦截器率先得到了response对象。而在上方的代码中,只是对request和response进行了日志打印,并没有实际操作。

但是事实上,拦截器拿到了request之后,可以对request进行重写,可以添加,移除,替换请求头,也能对response的header进行重写,改变response的body。佐证如下,

文档英文原文:

java
Rewriting Requests

Interceptors can add, remove, or replace request headers. They can also transform the body of those requests that have one. For example, you can use an application interceptor to add request body compression if you're connecting to a webserver known to support it.

重写请求:

拦截器可以 添加,移除,或者替换 请求头里面的内容。如果请求有请求体(post请求),那么它还可以多请求体里面的内容做转换,比如,你可以给你的应用增加一个”内容压缩的拦截器“,对请求体中的内容进行压缩,前提是你请求的服务器能支持你这种压缩后的请求内容,所以要实现类型功能请后端必须沟通好,比如前端采用什么样的压缩算法,后端接收到请求后,采用相同的算法解压。不过一般来说我们都不会采用这种拦截器,因为okhttp 默认就支持GZIP压缩。

例如:一个GZIP 拦截器,实现对 请求体进行压缩

final class GzipRequestInterceptor implements Interceptor {
 @Override public Response intercept(Interceptor.Chain chain) throws IOException {
 //第一步通过 chain 得到 Request 对象
 Request originalRequest = chain.request();
 // 如果该请求没有请求体,或者 如果请求头中指明了内容编码格式不为gzip,那么就不压缩
 if (originalRequest.body() == null || (originalRequest.header("Content-Encoding") != null && !originalRequest.header("Content-Encoding").toLowerCase().equals("gzip") ) {
 return chain.proceed(originalRequest);
 }

 /**
 *如果包含请求体,并且没有指明请求体中的内容编码格式,那么我们采用 gzip 压缩
 *既然要采用gzip 压缩,因此得在请求头写明内容编码给格式gzip,这样服务器才知道,
 *客服端发送过来的请求是用gzip算法压缩过的,服务器采用用gzip算法解压
 */
 Request compressedRequest = originalRequest.newBuilder()
 .header("Content-Encoding", "gzip")
 //1\. originalRequest.method() 返回原来的请求方式,post 或者 get
 //2\. gzip(originalRequest.body()),调用自己写好的gizp 压缩算法,对requestbody 压缩
 .method(originalRequest.method(), gzip(originalRequest.body()))
 .build();
 // 调用 chain.proceed 方法把重写后的 Request 对象发送下一个拦截器。
 // 下一个拦截器收到的Request 就是被重写后的Request.
 return chain.proceed(compressedRequest);
 }
​

 // Gzip压缩算法
 private RequestBody gzip(final RequestBody body) {
 return new RequestBody() {
 @Override public MediaType contentType() {
 return body.contentType();
 }
​
 @Override public long contentLength() {
 return -1; // We don't know the compressed length in advance!
 }
​
 @Override public void writeTo(BufferedSink sink) throws IOException {
 BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
 body.writeTo(gzipSink);
 gzipSink.close();
 }
 };
 }
} 

这样就是实现了通过拦截器,把请求拦截下来,对请求体里面的内容进行压缩,压缩后发送给服务器。

Rewriting Responses

Symmetrically, interceptors can rewrite response headers and transform the response body. This is generally more dangerous than rewriting request headers because it may violate the webserver's expectations!

重写响应:

相应的,拦截器可以重写服务器返回的相应头,响应体,虽然你可以重写响应头,但是一般不建议这么做,因为重写响应头可能会违反了网络服务器的期望,比如服务器的响应头里面指明该响应式不能被缓存,你却改写为了支持缓存,这就违背了服务器的期望,同样如果服务器的响应头里面指明了,响应式html 格式,你却改成json。那么负责解析的拦截器解析时必然会出问题,把一个html转成一个json对象肯定会出问题。

综上所述,拦截器的作用就是:在把请求发送出去之前,可以对reqeust 进行重写,在应用拿到response之前,先获得response,对其中某些数据进行监控,在有必要的情况下,对response的某些内容(比如response的header,body,response内的request的header,body)进行更改。

既然知道了拦截器的原理和作用,那么我们在anroid 开发过程中可以采用拦截器实现那些功能呢?最常见的有一下:

  1. 实现Cookie 的保存和读取

  2. 实现公共请求头和公共参数的统一添加。

拦截器分类

总体上来说okhttp3 可以分为2大类:

  1. 系统拦截器(核心拦截器)。

  2. 自定义拦截器:其中有分为:

    1. 应用拦截器

    2. 网络拦截器

    系统拦截器可以多次执行,自定义拦截器只能执行一次

OkhttpCore(内置5大核心拦截器)

上图拦截器流程示意图中,其中OkhttCore 包含了五大核心拦截器,它们承担了整个网络请求的全部工作。

1.RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor 的作用,看到该拦截器的名称就知道,它就是一个负责失败重连和重定向的拦截器。它是 Okhttp 内置的第一个拦截器,通过 while (true) 的死循环来进行对异常结果或者响应结果判断是否要进行重新请求。

引用一篇网上的重试机制

2.BridgeInterceptor

BridgeInterceptor 为用户构建的一个 Request 请求转化为能够进行网络访问的请求,同时将网络请求回来的响应 Response 转化为用户可用的 Response。比如,涉及的网络文件的类型和网页的编码,返回的数据的解压处理等等。

3.CacheInterceptor

CacheInterceptor 根据 OkHttpClient 对象的配置以及缓存策略对请求值进行缓存。

可以参考http 的缓存机制:

4.ConnectInterceptor

ConnectInterceptor 在 OKHTTP 底层是通过 SOCKET 的方式于服务端进行连接的,并且在连接建立之后会通过 OKIO 获取通向 server 端的输入流 Source 和输出流 Sink。

5.CallServerInterceptor

CallServerInterceptorConnectInterceptor 拦截器的功能就是负责与服务器建立 Socket 连接,并且创建了一个 HttpStream 它包括通向服务器的输入流和输出流。而接下来的 CallServerInterceptor 拦截器的功能使用 HttpStream 与服务器进行数据的读写操作的。

自定义拦截器

1.应用拦截器 Application Interceptor

2.网络拦截器 NetWork Interceptor

他们的异同:

相同点

1)都能对server返回的response进行拦截

2)这两种拦截器本质上都是基于Interceptor接口,由开发者实现这个接口,然后将自定义的Interceptor类的对象设置到okhttpClient对象中,所以,本质上没什么不同,都是Interceptor的实现类的对象。

3)两者都会被add到OkHttpClient内的一个ArrayList中。当请求执行的时候,多个拦截器会依次执行(list本身就是有序的)。

不同点

1)okhttpClient添加两种拦截器的api不同。添加应用拦截器的接口是addInterceptor(),而添加网络拦截器的接口是addNetworkInterceptor().

2)两者负责的区域不同,从最上方图中可以看出,应用拦截器作用于okhttpCore到Application之间,网络拦截器作用于 network和okhttpCore之间

3)在某种特殊情况下(比如:访问了一个url,结果这个url发生了重定向),网络拦截器有可能被执行多次,但是 不论任何情况,application只会被执行一次。

Okhttp 线程池和连接池

线程池:

okhttp 内部采用线程池和链接池的方式来解决多并发的问题.当我们采用 enqueue 方法发送一个异步请求时,最终会调到 Dispatcher 类中的 enqueue 方法,然后在 enqueue 方法中调用了promoteAndExecute方法.

Dispatcher 类中创建了一个线程池和几个队列:

public final class Dispatcher {
 private int maxRequests = 64; // 最大请求并发为64
 //host 指的就是域名例如:wanandroid.com. 就是说okhttp中,域名相同的请求最大并发数为5,超过就进入
 // 等待队列等待,也就是说,如果如果baseurl 一样的请求,okhttp 最多同时支持5个。
​
 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 readyAsyncCalls = new ArrayDeque<>();//待执行异步任务队列
​
 //运行中异步任务队列
 /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
 private final Deque runningAsyncCalls = new ArrayDeque<>();
​
 //运行中异步任务队列
 /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
 private final Deque 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<>(), Util.threadFactory("OkHttp Dispatcher", false));
 }
 return executorService;
 } 

参数说明一下:

1.readyAsyncCalls:待执行异步任务队列

2.runningAsyncCalls:运行中异步任务队列

3.runningSyncCalls:运行中同步任务队列

4.executorService:任务队列线程池

可以看到OKhttp中线程池的特点属性,此线程池的核心线程数为0,最大线程数量为Integer.MAX_VALUE,线程空闲时间只能活60秒, 然后用了SynchronousQueue队列,这是一个不存储元素的阻塞队列, 也就是说有任务到达的时候,只要没有空闲线程,就会创建一个新的线程来执行任务

执行一个异步请求:

  OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("http://myproject.com/helloworld.txt")
                .build();
        client.newCall(request).enqueue(new Callback() { // 异步
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d("OkHttp", "Call Failed:" + e.getMessage());
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d("OkHttp", "Call succeeded:" + response.message());
            }

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 方法:

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 和这次的异步请求AsyncCall 的host 相同,那么返回
           *返回后把之前的异步请求的callsPerHost(一个计数器AtomicInteger,线程安全的) 赋值给 这次的           *callsPerHost,这样,本次的AsyncCall 和之前的AsyncCall的callsPerHost 指向同一个对象
           *这就实现了所有host相同的异步请求都用同一个计数器,只有其中某一个进入执行队列开始执行就加一            *asyncCall.callsPerHost().incrementAndGet(),一旦执行完成就减一                           *call.callsPerHost().decrementAndGet(),该方法在Dispatcher的 finish 方法被调用
           *
          */

        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
     // 调用 promoteAndExecute 方法,去执行任务。
    promoteAndExecute();
  }

Dispatcher 的promoteAndExecute 方法:

  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));
    // 定义一个空的可执行的arraylist,用于把等待队列中满足条件的任务加入到里面,然后交给线程池去执行
    List executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

         // 如果正在执行的队列中的数大于64 ,那么直接跳出循环,
        if (runningAsyncCalls.size() >= maxRequests) //// Max capacity.
            break; 
          // 如果正在执行的队列runningAsyncCalls中,相同host 请求数量超过5就等待。
          // 等待相同host 的其他request执行完成。
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) // Host max capacity.
            continue; 

         // 如果满足就从等待队列中移除,
        i.remove();
         // 每次加一,对应上面的if 判断,如果超过5就得等待。加一后就准备进入执行队列里面开始执行
        asyncCall.callsPerHost().incrementAndGet();
          //加入到可执行的arraylist中。
        executableCalls.add(asyncCall);
          // 加入到执行队列中
        runningAsyncCalls.add(asyncCall);
      }
        // 如果执行队列中的size 大于0 ,表示正在执行网络请求。
      isRunning = runningCallsCount() > 0;
    }

      //循环从只执行的arraylist中取出任务交给 线程池去执行
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
       // 交给线程池去执行
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

总的来说就是 okhttp 内部采用了一个线程池来处理并发的网络请求,线程池的核心线程数为0,最大线程数为 Integer.MAX_VALUE = 2147483647. 空闲线程存活的时间为60秒,也就是说,超过60秒后,没有新的网络请求,如果之前队列里面的任务也已经执行完成,那么线程池里面的所有线程都要销毁,通过okhttp 创建线程池的方法可以看出,几乎是没有最大线程数量的限制,那是不是意味着同时可以并发2147483647个请求呢?其实不是。虽然最大线程数量没有限制,但不意味着可以同时并发无限多个请求。通过上面代码我们也发现了,执行异步的请求的顺序是这样的:

  1. Okhttpclient.newCall(request) 创建一个 RealCall 对象,一个真正的发起网络的对象
 OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url("http://myproject.com/helloworld.txt")
                    .build();
    client.newCall(request).enqueue(new Callback()
  1. 调用RealCall 的 enqueue ,传入一个CallBack 的回调。
client.newCall(request).enqueue(new Callback() 
  1. 通过Disapther 分发器,把这次请求封装成一个AsyncCall对象,然后入队。AsyncCall里面包含了这次真正的请求RealCall, 和 回调CallBack .
public void enqueue(Callback responseCallback) {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Exe9cuted");
          executed = true;
        }
        transmitter.callStart();
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
      }

    //AsyncCall 可以 Realcall 的内部类,所有AsyncCall持有了Realcall的 引用
    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

        //用于计数,相同host的请求都共用这一个对象进行计数,并且线程安全的。
        private volatile AtomicInteger callsPerHost = new AtomicInteger(0);

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

4.在Dispatcher 的 enqueue 方法中,先把异步请求加入到等待队列readyAsyncCalls中,如果本次请求不是websocket,那么去正在执行的队列和等待队列中找之前的请求中是否存在host 一样的异步请求(注意是异步请求,同步请求不包括),先找正在执行的,找到了直接返回。正在执行的队列里面找不到再去等待队列中找,找到了直接返回。如果两个队列都找不到返回null,返回null 说明这个host的 请求时第一次发送。如果找到了,那么把之前请求的计数器 callsPerHost 的引用赋值个当期请求,这样才能保证所有host 一样的请求都用同一个计数器。

void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);

      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
  }

// AsyncCall.java ,共用同一个计数器,实现相同host 共用一共。线程安全
void reuseCallsPerHostFrom(AsyncCall other) {
      this.callsPerHost = other.callsPerHost;
    }
  1. 把相同host 的请求指向同一个计数器后,执行promoteAndExecute 方法,在该方法中:

    1. 第一步创建一个 executableCalls 空的Arraylist,用于后面遍历等待队列中任务是,把满足条件的任务加入到数组中,然后统一交给线程池去执行。
    2. 循环遍历等待队列中的任务,如果当前正在执行的任务数量大于maxRequests 64,那么直接跳循环,executableCalls 元素为空,所以不做任何操作,相当于这次请求被加入到等待队列,到此结束。知道有任务完成后在Dispatcher.finish 方法中会再次调用promoteAndExecute方法。达到轮训效果。
    3. 如果当前正在执行的任务数量不大于64,那么继续第二个判断,判断目前正在执行的任务中,和当前请求的host 相同的任务数量是否超过5个,超过5个就跳过该循环,继续下个循环。如果不超过就从等待对队列移除,并且让计数器加一,同时加入到executableCalls 中
    4. 如果executableCalls 不为空,循环遍历,把任务交给线程池去执行。
      private boolean promoteAndExecute() {
        assert (!Thread.holdsLock(this));
     // 定义一个空的可执行的arraylist,用于把等待队列中满足条件的任务加入到里面,然后交给线程池去执行
        List executableCalls = new ArrayList<>();
        boolean isRunning;
        synchronized (this) {
          for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall asyncCall = i.next();
         
             // 如果正在执行的队列中的数大于64 ,那么直接跳出循环,
            if (runningAsyncCalls.size() >= maxRequests) //// Max capacity.
                break; 
              // 如果正在执行的队列runningAsyncCalls中,相同host 请求数量超过5就等待。
              // 等待相同host 的其他request执行完成。
            if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) // Host max capacity.
                continue; 
         
             // 如果满足就从等待队列中移除,
            i.remove();
             // 每次加一,对应上面的if 判断,如果超过5就得等待。加一后就准备进入执行队列里面开始执行
            asyncCall.callsPerHost().incrementAndGet();
              //加入到可执行的arraylist中。
            executableCalls.add(asyncCall);
              // 加入到执行队列中
            runningAsyncCalls.add(asyncCall);
          }
            // 如果执行队列中的size 大于0 ,表示正在执行网络请求。
          isRunning = runningCallsCount() > 0;
        }
     
          //循环从只执行的arraylist中取出任务交给 线程池去执行
        for (int i = 0, size = executableCalls.size(); i < size; i++) {
          AsyncCall asyncCall = executableCalls.get(i);
           // 交给线程池去执行
          asyncCall.executeOn(executorService());
        }
    
        return isRunning;
      }
    
  2. asyncCall.executeOn 方法中,线程池调用executorService.execute(this);去执行当前任务。

      void executeOn(ExecutorService executorService) {
          assert (!Thread.holdsLock(client.dispatcher()));
          boolean success = false;
          try {
             // 把这个runable 对下交给线程池,AsyncCall 实现了Runable 接口
            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) {
                // 如果线程池不接受该runable,调用finished。继续去等待队列里面取任务
              client.dispatcher().finished(this); // This call is no longer running!
            }
          }
        }
    
  3. executorService.execute(this); 会让线程去调用AsyncCall 的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 {
                // 不管是否执是否成功,都调用finished。继续去等待队列里面取任务
              client.dispatcher().finished(this);
            }
          }
        }
    
  4. Dispatcher 的 finish 方法中,对之前的计数器惊喜减一,如果减一,之前因为相同host 最大并发不能超过5而等待的异步任务就没有机会执行了。

       /** Used by {@code AsyncCall#run} to signal completion. */
        void finished(AsyncCall call) {
          call.callsPerHost().decrementAndGet();
          finished(runningAsyncCalls, call);
        }
    
  5. Finished 方法中又调用了重载的 finished(runningAsyncCalls, call),该方法中调用了上面5步中的promoteAndExecute(),继续去等待队列里面取等待执行的任务然后交给线程池去执行。

       private  void finished(Deque calls, T call) {
          Runnable idleCallback;
          synchronized (this) {
            if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
            idleCallback = this.idleCallback;
          }
         //继续去等待队列中取任务
          boolean isRunning = promoteAndExecute();
      
          if (!isRunning && idleCallback != null) {
            idleCallback.run();
          }
        }
    

你可能感兴趣的:(有关OKHttp3的原理(源码浅析))