本质

拼装 Http 报文发送和响应请求。主要实现方式:责任链,内部使用 okio 实现 I/O 操作
引入:

  1. const val okhttp = "com.squareup.okhttp3:okhttp:4.9.0"

参考阅读

语雀内容

核心

使用责任链模式构造拦截器,每个拦截器都有着「前置」,「中置」,「后置」工作。

版本支持

Android 5.0+ / API 21+;**Java 8+
原因:Google在 Android 5.0 中添加了对
TLS v1.2** 的支持。Oracle在 Java 8 中添加了它。因此在 OkHttp 3.13 中,要求主机平台具有对 TLS v1.2 的内置支持。
OkHttp 3.13 Requires Android 5+

OkHttpClient

🐶 OkHttp - 图2发送请求和接收响应的 call 的工厂,可以使用构造器配置网络请求的配置,如超时时间等。

OkHttpClient 应该被共享,使用时保持单例

Dispatcher

🐶 OkHttp - 图3异步请求的调度器,内部使用 ExecutorService

最大请求数为:64,每个 host 的最大请求数为:5。

ExchangeCodec 读写报文的工具

🐶 OkHttp - 图4类型:接口
用途:encode HTTP 请求,decode HTTP 响应

Exchange 读写管理员,内部调用 codec

传送一个 HTTP 请求和响应对,该层连接 管理 和 ExchangeCodec(实际处理 I/O) 的事件

RealInterceptorChain

🐶 OkHttp - 图5拦截器链,包含整个拦截器链:所有的应用拦截器(OkHttp 核心),所有的网络拦截器(最终的网络调用者)

  • proceed(request: Request): Response

image.png

RealCall

🐶 OkHttp - 图7 该类是 应用层与网络层之间的桥梁,提供了连应用层使用的方法:连接请求响应 。该类还支持异步取消。

  • getResponseWithInterceptorChain()

image.png
image.png
image.png
**

自定义 Interceptor

自定义 Interceptor 分为 client Interceptornetwork Interceptor,前者为这个拦截链的最前端,后者在 ConnectInterceptor 与 CallServerInterceptor 之间,做网络调试使用,不常用

实现 Interceptor 接口,重写 intercept 方法

image.png

1. RetryAndFollowUpInterceptor

🐶 OkHttp - 图12

  • 前置

连接准备:创建 ExchangeFinder,在后续拦截器中使用

call.enterNetworkInterceptorExchange``_(_``request``, ``newExchangeFinder``_)_
image.png

  • 中置

response = realChain.proceed(_request)_

  • 后置
  • 错误重试./重定向

    2. BridgeInterceptor

    🐶 OkHttp - 图14app 代码 转换为 网络报文的桥梁

  • 前置

根据用户请求构建网络请求
image.png

  • 中置

networkResponse = chain.proceed(_requestBuilder.build())_

  • 后置

gzip 解开 body
image.png

3. CacheInterceptor

🐶 OkHttp - 图17

  • 前置

如果满足某些条件,提前返回
image.png

  • 中置

networkResponse = chain.proceed(networkRequest)

  • 后置

更新/保存 缓存
image.png

4. ConnectInterceptor

🐶 OkHttp - 图20打开与目标服务器的连接,并进入下一个拦截器。
image.png

  • ConnectInterceptor
    • initExchange()
      • ExchangeFinder.find()
        • ExchangeFinder.findHealthyConnection()
          • ExchangeFinder.findConnection()
            • 第一次:call.connection 不为空且符合复用条件,直接使用并返回
            • 第二次:调用 callAcquirePooledConnection 拿非多路复用的链接
            • 第三次:调用 callAcquirePooledConnection 非多路/多路 都拿,本次和上次拿到则直接返回
            • 第四次:上面没拿到,直接自己创建
            • 第五次:再次 调用 callAcquirePooledConnection 拿多路复用的链接,如果拿到将第四次创建的丢弃,将 result 返回


RealCall.initExchange()

image.png

ExchangeFinder.find()

image.png

ExchangeFinder.findHealthyConnection()

image.png

ExchangeFinder.findConnection()

拿 5 次 第一次:call.connection 不为空且符合复用条件,直接使用并返回 第二次:调用 callAcquirePooledConnection 拿非多路复用的链接 第三次:调用 callAcquirePooledConnection 非多路/多路 都拿,本次和上次拿到则直接返回 第四次:上面没拿到,直接自己创建 第五次:再次 调用 callAcquirePooledConnection 拿多路复用的链接,如果拿到将第四次创建的丢弃,将 result 返回

    1. call.isCanceled(),抛异常
    1. 判断是否有可用连接 call.connection!=null ,且满足条件,直接使用image.pngimage.png
    1. 如果 result == null,第一次尝试调用 connectionPool.callAcquirePooledConnectionroutes 为 nullrequireMultiplexed 为 false拿 非多路复用的链接 。如果返回值为 true 则将 foundPooledConnection 赋值为 true

image.png

    1. 如果 newRouteSelection 为 true 第二次尝试调用 connectionPool.callAcquirePooledConnection,routes 有值requireMultiplexed 为 false拿多路复用或非多路复用均可的链接。如果返回值为 true 则将 foundPooledConnection 赋值为 true

image.png

    1. 如果 foundPooledConnection 为 true 直接返回 result

image.png

    1. 前面都没返回,自己创建一个 RealConnection 并赋值给 result ,调用 result.connect() 自己连接

image.png
image.png

    1. 再次调用 connectionPool.callAcquirePooledConnection(requireMultiplexed 参数为 true),拿多路复用的链接。如果为 true,将之前创建的丢弃。应对极端情况,两个 http2 连接同时请求,创建连完成后其中一个放入池中,另一个可以废弃自己的连接,使用复用的

image.png

RealConnectionPool.callAcquirePooledConnection()

查看是否有可复用的连接:
image.png

RealConnection.isEligible

连接是否合格:

如何判断连接是否合格

  1. 连接上的请求数没有超限(http1 当前连接没请求;http 2 当前连接加上这一个没有超限) 2. 同一方式线连接到同一主机(端口,tls,代理,等信息一样)

5. CallServerInterceptor

链上的最后一个拦截器,生成发送到服务器的网络请求。

操作 exchange,写入请求和解析响应
image.png

RealConnection

🐶 OkHttp - 图35

isEligible()

如何判断连接是否合格

  1. 连接上的请求数没有超限(http1 当前连接没请求;http 2 当前连接加上这一个没有超限) 2. 同一方式线连接到同一主机(端口,tls,代理,等信息一样)

newCodec()

image.png
根据 HTTP1 与 HTTP2 使用不同的 ExchangeCodec

connect()

image.png
1.如果需要连接 Tunnel,则调用 connectTunnel,否则调用 connectSocket
2.搭建协议,调用 establishProtocol

connectTunnel

有时候建立 HTTP 连接,目的是访问 HTTPS 的连接,此时需要一个 HTTP 的中间服务器,以 HTTP 访问中间 服务器,让中间服务器建立 HTTPS 连接。这个中间服务器便是 Tunnel
image.png
1.创建 TCP 连接
2.创建 Tunnel

connectSocket

image.png
创建 Socket,连接 Socket

establishProtocol()


搭建协议

  • 不是 TLS

image.png

  • 是 TLS

image.png