什么是 Okhttp?

所谓的 Okhttp 就是通过代码的方式实现了各种协议,将这些通信协议封装起来,让我们可以快速地用代码来实现。

Okhttp 好处

  • 支持 HTTP1、HTTP2、Quic以及 WebSocket

之所以支持,是因为 Okhttp 的源码里面对这些协议的规则进行了实现。

  • 连接池复用底层 TCP连接,减少请求延时

建立 TCP 连接是需要时间的,okHTTP 源码中对已经连接的 TCP,其实在代码中的体现就是 Socket 进行了缓存,再次请求同一地址的时候就不用重复建立连接了,从而减少请求延时。

  • 无缝的支持 GZIP 减少数据流量

其实这是 HTTP 协议的内容,HTTP 协议中可以在请求头中规定是否支持数据压缩,Okhttp 就把这个请求头封装进去了,告诉服务器,我可以接受一个 GZIP 压缩的数据报文。

  • 缓存响应数据减少重复的网络请求

这也是 HTTP 协议中定义的内容,有对应的字段表示

  • 请求失败自动重试主机的其他 ip,自动重定向

同样也是 HTTP 协议中定义的内容。

这些好处,其实就是 okhttp 利用 HTTP 报文格式中规定的内容,然后进行处理,完成这些规定。如果没有处理的话,任凭服务器发送来的报文是什么,如果统统不管的话,那也是没用的。

因此再次说明 HTTP 协议只是规定了你我通信要发送的内容需要遵从什么样的格式,至于我有没有根据内容,实现对应的功能,那就不是 HTTP 协议的范围了。

Okhttp 使用

    // 步骤 1   
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(60,TimeUnit.SECONDS)
                .connectTimeout(60,TimeUnit.SECONDS)
                .cache(new Cache(new File("xx"),1024))
                .build();
    // 步骤 2
        Request request = new Request.Builder()
                .url(AppConfig.URL.url_get)
                .build();
    // 步骤 3    
        Call call = okHttpClient.newCall(request);
   // 步骤 4    
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });

okhttp 的使用一般就是上面 4 步。

第一步:创建 okhttpClient ,也就是一个客户端,同时也是也 Call 的工厂,主要作用就是记录一些配置内容,比如 :连接超时时间、读取超时时间、缓存地址等等这种配置。这个对象可以共用,不用每次都创建。

第二步:就是创建一个请求报文。

第三步:就是通过 Call 工厂 okhttpClient 和 请求报文,构建出一个准备好要执行的请求。通过 Call 来发起网络请求。

第四步:发起网络请求

虽然就这样简单的四步,但是代码设计的非常好,首先做到了功能分离!单一职责,有三个类 OkhttpClient 、Request 、Call 分别负责不同的职责,非常清晰。

核心原理分析

okhttp 整个大的流程核心就是一个分发器 Dispatcher 和 拦截器 interceptors 下面分别分析

Dispatcher

分发器用于执行我们网络请求的异步任务,Dispatcher 有 4 个核心成员对象 :

  • ExecutorService

线程池,用于执行异步任务

  • 三个队列 Deque

三个队列,分别是用于存储 正在执行的异步任务、正在执行的同步任务、准备执行的异步任务

流程图:

alt

Dispatcher 的整个执行流程就如上图所示,下面来结合源码分别介绍。

// 异步请求
call.enqueue(new Callback() );
// 这个时候会执行 RealCall 下面的方法

alt

这个时候就进入 Dispatcher 分发器

alt

重点来了

首先这个方法是个同步方法,有个判断,判断这个请求是放入 running 队列还是  ready 队列。

图中的1 就是判断条件,如果 running 队列中的 call 小于最大请求数(默认 64)并且对同一地址的请求小于 最大主机请求数(默认5),这个时候就放入 running 队列,直接交给线程池来执行 请求。否则加入 ready 队列,等待请求。

然后看 executorService().execute(call) 这一步其实就是交给线程池执行,最终执行的是 AsyncCall 的 execute() 方法

注意这个方法是在子线程中执行的。

alt

1: 是真正的触发网络请求,进入下一个核心点 “拦截器”。 2:可以看到 2 是在 finally 中执行的,也就是总是会执行到。

alt

1:执行完毕后就把 call 从 runing 队列中移除了,然后执行 2

alt

这里会根据条件循环判断 ready队列中的 call 是否能添加到 running 队列中执行。

到此整个分发器的执行流程就结束了!

总结 对于 Dispatcher 分发器核心点就是一个线程池、维持请求队列。

添加一个请求的时候会判断正在请求的数量,如果条件满足就放入线程池执行,否则放入等待队列,等待执行。

**********

alt