type
status
date
slug
summary
tags
category
password
icon
OkHttp 使⽤⽅法简介
- 创建⼀个
OkHttp
的实例
- 创建
Request
- 创建
Call
并发起⽹络请求
OkHttp 源码总结
OkHttpClient
相当于配置中⼼,所有的请求都会共享这些配置(例如出错是否重试、共享的连接池)。OkHttpClient
中的配置主要有:Dispatcher dispatcher
:调度器,⽤于调度后台发起的⽹络请求,有后台总请求数和单主机总请求数的控制。ConnectionPool connectionPool
:线程池,线程的批量管理,复用。List<Protocol> protocols
:⽀持的应⽤层协议,即 HTTP/1.1、HTTP/2 等。List<Interceptor> interceptors
:⼤多数时候使⽤的 Interceptor 都应该配置到这⾥。List<Interceptor> networkInterceptors
:直接和⽹络请求交互的 Interceptor 配置到这⾥,例如如果你想查看返回的 301 报⽂或者未解压的 Response Body,需要在这⾥看。boolean retryOnConnectionFailure
:在请求失败的时候是否⾃动重试。注意,⼤多数的请求失败并不属于 OkHttp 所定义的「需要重试」,这种重试只适⽤于「同⼀个域名的多个 IP 切换重试」「Socket 失效重试」等情况Authenticator authenticator
:⽤于⾃动重新认证。配置之后,在请求收到 401 状态码的响应是,会直接调⽤authenticator
,⼿动加⼊Authorization header
之后⾃动重新发起请求。boolean followRedirects
:遇到服务器的重定向的要求是,是否⾃动重定向。boolean followSslRedirects
在重定向时,如果原先请求的是 http ⽽重定向的⽬标是 https,或者原先请求的是 https ⽽重定向的⽬标是 http,是否依然⾃动 follow。(记得,不是「是否⾃动 follow HTTPS URL 重定向的意思,⽽是是否⾃动 follow 在 HTTP 和 HTTPS之间切换的重定向)CookieJar cookieJar
:管理 Cookie 的控制器。OkHttp 提供了 Cookie 存取的判断⽀持(即什么时候需要存 Cookie,什么时候需要读取 Cookie,但没有给出具体的存取实现。如果需要存取 Cookie,你得⾃⼰写实现,例如⽤Map
存在内存⾥,或者⽤别的⽅式存在本地存储或者数据库。Cache cache
:Cache 存储的配置。默认是没有,如果需要⽤,得⾃⼰配置出 Cache 存储的⽂件位置以及存储空间上限。List<ConnectionSpec> connectionSpecs
:应⽤层⽀持的 Socket 设置,即使⽤明⽂传输(⽤于 HTTP)还是某个版本的 TLS(⽤于 HTTPS)。HostnameVerifier hostnameVerifier
:⽤于验证 HTTPS 握⼿过程中下载到的证书所属者是否和⾃⼰要访问的主机名⼀致。CertificatePinner certificatePinner
:⽤于设置 HTTPS 握⼿过程中针对某个Host 额外的的 Certificate Public Key Pinner,即把⽹站证书链中的每⼀个证书公钥直接拿来提前配置进 OkHttpClient ⾥去(具体填⼊的是证书的公钥的 hash)值,作为正常的证书验证机制之外的⼀次额外验证。int connectTimeout
:建⽴连接(TCP 或 TLS)的超时时间。int readTimeout
:发起请求到读到响应数据的超时时间。int writeTimeout
:发起请求并被⽬标服务器接受的超时时间。(为什么?因为有时候对⽅服务器可能由于某种原因⽽不读取你的 Request)
newCall(Request)
⽅法会返回⼀个RealCall
对象,它是Call
接⼝的实现。当调⽤RealCall.execute()
的时候,RealCall.getResponseWithInterceptorChain()
会被调⽤,它会发起⽹络请求并拿到返回的响应,装进⼀个Response
对象并作为返回值返回;RealCall.enqueue()
被调⽤的时候⼤同⼩异,区别在于enqueue()
会使⽤Dispatcher
的线程池来把请求放在后台线程进⾏,但实质上使⽤的同样也是getResponseWithInterceptorChain()
⽅法。
getResponseWithInterceptorChain()
⽅法做的事:把所有配置好的Interceptor
放在⼀个List
⾥,然后作为参数,创建⼀个RealInterceptorChain
对象,并调⽤chain.proceed(request)
来发起请求和获取响应。
- 在
RealInterceptorChain
中,多个Interceptor
会依次调⽤⾃⼰的intercept()
⽅法。这个⽅法会做三件事: - 对请求进⾏预处理
- 预处理之后,重新调⽤
RealIntercepterChain.proceed()
把请求交给下⼀个Interceptor
- 在下⼀个 Interceptor 处理完成并返回之后,拿到 Response 进⾏后续处理
- 从上到下,每级
Interceptor
做的事: - ⾸先是开发者使⽤
addInterceptor(Interceptor)
所设置的,它们会按照开发者的要求,在所有其他Interceptor
处理之前,进⾏最早的预处理⼯作,以及在收到Response
之后,做最后的善后⼯作。如果你有统⼀的 Header 要添加,可以在这⾥设置; - 然后是
RetryAndFollowUpInterceptor
:它会对连接做⼀些初始化⼯作,并且负责在请求失败时的重试,以及重定向的⾃动进行后续请求。它的存在,可以让重试和重定向对于开发者是⽆感知的; BridgeInterceptor
:它负责⼀些不影响开发者开发,但影响 HTTP 交互的⼀些额外预处理。例如,Content-Length 的计算和添加、gzip 的⽀持(Accept-Encoding: gzip)、gzip 压缩数据的解包,都是发⽣在这⾥;CacheInterceptor
:它负责 Cache 的处理。把它放在后⾯的⽹络交互相关Interceptor
的前⾯的好处是,如果本地有了可⽤的 Cache,⼀个请求可以在没有发⽣实质⽹络交互的情况下就返回缓存结果,⽽完全不需要开发者做出任何的额外⼯作,让 Cache 更加⽆感知;ConnectInterceptor
:它负责建⽴连接。在这⾥,OkHttp 会创建出⽹络请求所需要的 TCP 连接(如果是 HTTP),或者是建⽴在 TCP 连接之上的 TLS 连接(如果是 HTTPS),并且会创建出对应的HttpCodec
对象(⽤于编码解码 HTTP 请求);- 然后是开发者使⽤
addNetworkInterceptor(Interceptor)
所设置的,它们的⾏为逻辑和使⽤addInterceptor(Interceptor)
创建的⼀样,但由于位置不同,所以这⾥创建的Interceptor
会看到每个请求和响应的数据(包括重定向以及重试的⼀些中间请求和响应),并且看到的是完整原始数据,⽽不是没有加 Content-Length 的请求数据,或者 Body 还没有被 gzip 解压的响应数据。多数情况,这个⽅法不需要被使⽤,不过如果你要做⽹络调试,可以⽤它; CallServerInterceptor
:它负责实质的请求与响应的 I/O 操作,即往 Socket ⾥写⼊请求数据,和从 Socket ⾥读取响应数据;RealCall
其中包含内部类- Dispatcher:调度器
- 拦截器
Interceptor
- 拦截器链
Chain
RealInterceptorChain
Exchange
- RouteSelector
- RouteSelection
- Route
- http1: 同一个连接上面同时只能进行一个请求
- http2:同一个连接上面可以同时进行多个请求(多路复用)
- 当这个请求已经具有连接时
- 判断请求具有的连接是否符合当前连接的要求,比如http到https的重定向就不符合要求
- 不符合的需要释放掉当前的连接,走第2步新请求的逻辑
- 符合要求就直接返回请求自己具有的连接。
- 当这个请求是一个新请求,还没有连接时
- 尝试重连接池中获取一个不是多路复用的连接,获取到了直接返回
- 通过
RouteSelector
选出一组可用的路由(包含直连和代理),不管是否是多路复用的连接,都尝试获取,获取到了直接返回 - 自己创建一个连接
- 然后尝试获取只是多路复用的连接,避免两个可以多路复用的请求,同时创建了两个多路复用的连接,
- 如果发现别的请求已经帮创建好了,就临时存一下自己刚刚创建的连接。以免自己刚刚创建好,别人创建的是个坏的,就可以使用自己创建的。
- 如果上一步没有重复,就把新创建的连接添加到连接池
- Dispatcher 的作用:管理请求的分发和调度,控制并发请求数。
- 同步请求流程:直接执行,完成后从队列中移除。
- 异步请求流程:根据并发数和主机数决定是否立即执行或放入等待队列。
- 调度机制:通过
promoteAndExecute()
方法调度等待队列中的请求。 - 配置参数:可以通过
maxRequests
、maxRequestsPerHost
和executorService
自定义Dispatcher
的行为。
Okttp中关键类的作用和联系
内部包含了线程池去实际的执行任务(RealCall),并通过三个ArrayQueue<AsyncCall> 负责记录
即将执行异步任务
、 正在执行的异步任务
正在执行的同步任务
。为什么异步任务需要使用两个队列?
参考生产者消费者模型。
1 大厂面试必问之Okhttp请求复用与缓存方式洋解
2 大厂面试必问之OkHttp连接池复用原理
连接池复用原理
判断是否是同一个连接的条件:
主机、端口号、协议版本、是否使用代理等都相同才可以复用这个连接
一个新的请求,被封装到RealCall中,在请求时,会走一遍拦截器链,其中连接拦截器是一个单例,所有连接的获取和创建都在里面。
3 大厂面试必问之OkHttp责任链模式如何运行的
4 大厂面试必问之Dispatcher分发流程
- 作者:shuouyang
- 链接:https://notion-tree.vercel.app/article/fa9a95df-c586-409e-a0b8-bd6cb8e440f7
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章