本质
拼装 Http 报文发送和响应请求。主要实现方式:责任链,内部使用 okio 实现 I/O 操作
引入:
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
发送请求和接收响应的 call 的工厂,可以使用构造器配置网络请求的配置,如超时时间等。
OkHttpClient 应该被共享,使用时保持单例
Dispatcher
异步请求的调度器,内部使用 ExecutorService
最大请求数为:64,每个 host 的最大请求数为:5。
ExchangeCodec 读写报文的工具
类型:接口
用途:encode HTTP 请求,decode HTTP 响应
Exchange 读写管理员,内部调用 codec
传送一个 HTTP 请求和响应对,该层连接 管理 和 ExchangeCodec
(实际处理 I/O) 的事件
RealInterceptorChain
拦截器链,包含整个拦截器链:所有的应用拦截器(OkHttp 核心),所有的网络拦截器(最终的网络调用者)
- proceed(request: Request): Response
RealCall
该类是 应用层与网络层之间的桥梁,提供了连应用层使用的方法:连接,请求,响应 和 流。该类还支持异步取消。
- getResponseWithInterceptorChain()
自定义 Interceptor
自定义 Interceptor 分为 client Interceptor 和 network Interceptor,前者为这个拦截链的最前端,后者在 ConnectInterceptor 与 CallServerInterceptor 之间,做网络调试使用,不常用。
实现 Interceptor 接口,重写 intercept 方法
1. RetryAndFollowUpInterceptor
- 前置
连接准备:创建 ExchangeFinder,在后续拦截器中使用
call.enterNetworkInterceptorExchange``_(_``request``, ``newExchangeFinder``_)_
- 中置
response = realChain.proceed(_request)_
根据用户请求构建网络请求
- 中置
networkResponse = chain.proceed(_requestBuilder.build())_
- 后置
3. CacheInterceptor
- 前置
如果满足某些条件,提前返回
- 中置
networkResponse = chain.proceed(networkRequest)
- 后置
更新/保存 缓存
4. ConnectInterceptor
打开与目标服务器的连接,并进入下一个拦截器。
- ConnectInterceptor
- initExchange()
- ExchangeFinder.find()
- ExchangeFinder.findHealthyConnection()
- ExchangeFinder.findConnection()
- 第一次:call.connection 不为空且符合复用条件,直接使用并返回
- 第二次:调用 callAcquirePooledConnection 拿非多路复用的链接
- 第三次:调用 callAcquirePooledConnection 非多路/多路 都拿,本次和上次拿到则直接返回
- 第四次:上面没拿到,直接自己创建
- 第五次:再次 调用 callAcquirePooledConnection 拿多路复用的链接,如果拿到将第四次创建的丢弃,将 result 返回
- ExchangeFinder.findConnection()
- ExchangeFinder.findHealthyConnection()
- ExchangeFinder.find()
- initExchange()
RealCall.initExchange()
ExchangeFinder.find()
ExchangeFinder.findHealthyConnection()
ExchangeFinder.findConnection()
拿 5 次 第一次:call.connection 不为空且符合复用条件,直接使用并返回 第二次:调用 callAcquirePooledConnection 拿非多路复用的链接 第三次:调用 callAcquirePooledConnection 非多路/多路 都拿,本次和上次拿到则直接返回 第四次:上面没拿到,直接自己创建 第五次:再次 调用 callAcquirePooledConnection 拿多路复用的链接,如果拿到将第四次创建的丢弃,将 result 返回
- call.isCanceled(),抛异常
- 判断是否有可用连接 call.connection!=null ,且满足条件,直接使用
- 如果 result == null,第一次尝试调用
connectionPool.callAcquirePooledConnection
,routes 为 null,requireMultiplexed 为 false;拿 非多路复用的链接 。如果返回值为 true 则将foundPooledConnection
赋值为 true
- 如果 result == null,第一次尝试调用
- 如果 newRouteSelection 为 true 第二次尝试调用 connectionPool.callAcquirePooledConnection,routes 有值,requireMultiplexed 为 false;拿多路复用或非多路复用均可的链接。如果返回值为 true 则将
foundPooledConnection
赋值为 true
- 如果 newRouteSelection 为 true 第二次尝试调用 connectionPool.callAcquirePooledConnection,routes 有值,requireMultiplexed 为 false;拿多路复用或非多路复用均可的链接。如果返回值为 true 则将
- 如果 foundPooledConnection 为 true 直接返回 result
- 前面都没返回,自己创建一个 RealConnection 并赋值给 result ,调用 result.connect() 自己连接
- 再次调用 connectionPool.callAcquirePooledConnection(requireMultiplexed 参数为 true),拿多路复用的链接。如果为 true,将之前创建的丢弃。应对极端情况,两个 http2 连接同时请求,创建连完成后其中一个放入池中,另一个可以废弃自己的连接,使用复用的
RealConnectionPool.callAcquirePooledConnection()
RealConnection.isEligible
连接是否合格:
如何判断连接是否合格
- 连接上的请求数没有超限(http1 当前连接没请求;http 2 当前连接加上这一个没有超限) 2. 同一方式线连接到同一主机(端口,tls,代理,等信息一样)
5. CallServerInterceptor
链上的最后一个拦截器,生成发送到服务器的网络请求。
RealConnection
isEligible()
如何判断连接是否合格
- 连接上的请求数没有超限(http1 当前连接没请求;http 2 当前连接加上这一个没有超限) 2. 同一方式线连接到同一主机(端口,tls,代理,等信息一样)
newCodec()
根据 HTTP1 与 HTTP2 使用不同的 ExchangeCodec
connect()
1.如果需要连接 Tunnel,则调用 connectTunnel,否则调用 connectSocket
2.搭建协议,调用 establishProtocol
connectTunnel
有时候建立 HTTP 连接,目的是访问 HTTPS 的连接,此时需要一个 HTTP 的中间服务器,以 HTTP 访问中间 服务器,让中间服务器建立 HTTPS 连接。这个中间服务器便是 Tunnel
1.创建 TCP 连接
2.创建 Tunnel
connectSocket
创建 Socket,连接 Socket
establishProtocol()
搭建协议
- 不是 TLS
- 是 TLS