什么是 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<AsynCall>

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

流程图:

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

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

这个时候就进入 Dispatcher 分发器

重点来了

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

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

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

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

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

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

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

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

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

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

百大框架源码解析视频


学Android的小鱼
13 声望1 粉丝