okhttp核心原理分析(1)

更多文章分类

文章思维导图

前言

进行 okhttp 的核心源码分析,必须要搞清楚 http 协议以及相关的网络协议。这里只对协议容易混淆的地方进行说明。

首先我们要明确一点,要想让两台计算机进行通信,首先需要建立连接,也就是我们常说的三次握手。

计算机A 要想和计算机 B 进行通信,首先要知道计算机B 的IP 地址,知道 IP 地址后,就能访问计算机B,而要和计算机B上的那个程序通信,这个时候就需要 TCP 的地址了,也就是端口号。有了这两个信息,两者就可以建立连接了。其实这个时候就可以进行通信了。体现在代码中,就是通过 Socket 建立连接,然后进行读写操作。

这个时候也看出来了,所谓的 Socket 其实就是对 TCP/IP 建立连接通信的一种具体封装,不同的语言代码有不同的实现。有了 Socket 后,开发者就可以方便地进行网络连接通信了。Socket 本身并不是 TCP/IP网络协议中的协议

HTTP 协议就是在建立了 Socket 连接后规定了通信内容的格式,大家都遵从这个协议进行通信。

HTTP通信内容?

简单来说,所谓的 HTTP 通信就是建立 Socket 连接,然后把通信内容拼接成符合 HTTP 报文的内容发送出去。

什么是 okhttp

有了前言的内容,我们就可以理解什么是 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 来发起网络请求。

第四步:发起网络请求

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

核心原理分析

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) 这一步其实就是交给线程池执行,最终执行的是 AsyncCallexecute() 方法

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

1: 是真正的触发网络请求,进入下一个核心点 “拦截器”。(后面讲解)

2:可以看到 2 是在 finally 中执行的,也就是总是会执行到。

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

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

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

总结

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

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

后面我们会继续介绍下一个核心-----拦截器

posted @ 2021-01-28 16:14  sydMobile  阅读(692)  评论(0编辑  收藏  举报