OkHttp4.9.3
okhttp内部逻辑流程图:
1. 整体结构
主要类有:
- OkHttpClient
- Request 和 Response
- RealCall
简单介绍:
- OkHttpClient:核心管理类,所有内部逻辑和对象归OkHttpClient统一管理,由Builder构造器生成。
- Request 和 Response:两个累完全符合http协议所定义的请求内容和响应内容
- Request:发送请求封装类,包含url、method、headers、body
- Response:返回结果,包含code、message、headers、body
- RealCall:OkHttp 的应用层和网络层之间的桥梁。负责请求的调度(同步走当前线程发送请求,异步使用OkHttp内部的线程池进行),同时负责构造内部逻辑责任链,并执行责任链相关逻辑,知道获取结果。虽然OkHttpClient师整个OkHttp的核心管理类,但是真正发出请求并且组织逻辑的师RealCall类,它同时肩负了调度和责任链组织的两大重任。
重点:
RealCall两个重要方法:execute() 和 enqueue()
- execute() :处理同步请求
- enqueue() :处理异步请求。只是通过异步线程和callback做了一个异步调用的封装,最终逻辑还是会调用到execute(),然后会调用 getResponseWithInterceptorChain()获得请求结果
getResponseWithInterceptorChain()承载了整个请求的核心逻辑。是okhttp的大体请求流程。
internal fun getResponseWithInterceptorChain(): Response {
// 创建一个Interceptors拦截器的列表
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
//创建RealInterceptorChain,传入拦截器列表
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
try {
//调用RealInterceptorChain.proceed,传入参数:originalRequest(应用程序未受重定向或身份验证标头的影响的原始请求)
//获取返回结果
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
Interceptors拦截器的List列表,按顺序依次将:
- client.Interceptors
- RetryAndFollowUpInterceptor,
- BridgeInterceptor
- CacheInterceptor
- ConnectInterceptor
- client.networkInterceptors(forWebSocket==false)
- CallServerInterceptor
结论:okhttp将整个请求的复杂逻辑切成了一个个独立的模块并命名为连接器(Interceptor),通过责任链的设计模式串联到一起,最终完成了请求获取先买个因结果。
2. 拦截器
创建完RealInterceptorChain后,调用RealInterceptorChain.proceed获得响应结果,其流程如下:
//copy函数
internal fun copy(
index: Int = this.index,
exchange: Exchange? = this.exchange,
request: Request = this.request,
connectTimeoutMillis: Int = this.connectTimeoutMillis,
readTimeoutMillis: Int = this.readTimeoutMillis,
writeTimeoutMillis: Int = this.writeTimeoutMillis
) = RealInterceptorChain(call, interceptors, index, exchange, request, connectTimeoutMillis,
readTimeoutMillis, writeTimeoutMillis)
//RealInterceptorChain.proceed
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
calls++
if (exchange != null) {
check(exchange.finder.sameHostAndPort(request.url)) {
"network interceptor ${
interceptors[index - 1]} must retain the same host and port"
}
check(calls == 1) {
"network interceptor ${
interceptors[index - 1]} must call proceed() exactly once"
}
}
// Call the next interceptor in the chain.
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
if (exchange != null) {
check(index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
}
check(response.body != null) {
"interceptor $interceptor returned a response with no body" }
return response
}
其本质上就是通过将多个拦截器以责任链的方式来一层层调用,上一个拦截器处理完后将就将结果传给下一个拦截器,直到最后一个拦截器(即 CallServerInterceptor )处理完后将 Response 再一层层往上传递。
具体步骤
- 拦截器按照添加顺序依次执行
- 拦截器的执行从RealInterceptorChain.proceed()开始,进入到第一个拦截器的执行逻辑
- 每个拦截器在执行之前,会将剩余尚未执行的拦截器组成新的RealInterceptorChain
- 拦截器的逻辑被新的责任链调用next.proceed()切分为start、next.proceed、end这三个部分依次执行
- next.proceed() 所代表的其实就是剩余所有拦截器的执行逻辑
- 所有拦截器最终形成一个层层内嵌的嵌套结构
拦截器都继承了Interceptor接口:
fun interface Interceptor {
@Throws(IOException::class)
fun intercept(chain: Chain): Response //每个拦截器具体逻辑实现在intercept()
companion object {
/**
* ```kotlin
* val interceptor = Interceptor { chain: Interceptor.Chain ->
* chain.proceed(chain.request())
* }
* ```
*/
inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor =
Interceptor {
block(it) }
}
interface Chain {
fun request(): Request
@Throws(IOException::class)
fun proceed(request: Request): Response
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
fun connection(): Connection?
fun call(): Call
fun connectTimeoutMillis(): Int
fun withConnectTimeout(timeout: Int, unit: TimeUnit): Chain
fun readTimeoutMillis(): Int
fun withReadTimeout(timeout: Int, unit: TimeUnit): Chain
fun writeTimeoutMillis(): Int
fun withWriteTimeout(timeout: Int, unit: TimeUnit): Chain
}
}
拦截器主要分为两类:自定义拦截器和OkHttp内部拦截器,自定义拦截器可以在创建OkHttpClient.Builder时,通过addInterceptor 和 addNetworkdInterceptor 添加自定义的拦截器
自定义拦截器:
- client.Interceptors
- client.networkInterceptors:满足 forWebSocket==false 才添加
OkHttp内部拦截器:
- RetryAndFollowUpInterceptor:失败和重定向拦截器
- BridgeInterceptor:封装request和response拦截器
- CacheInterceptor:缓存相关的拦截器,负责读取缓存直接返回、更新缓存
- ConnectInterceptor:连接服务,负责和服务器建立连接,真正的请求网络
- CallServerInterceptor:执行流操作(写出请求体、获取相应数据)负责向服务器发送请求数据,从服务器读取响应数据,进行http请求报文的封装和请求报文的解析
2.1 RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor:失败和重定向拦截器
为什么要重新发送请求
- 路由失败,这里稍微解释一下路由就是一个web服务可能会有代理或者多个IP地址,这些就可以组成所谓的路由,只要我们能够连通其中任意一条路由即可与服务器通信,所以在路由失败之后还可以选择其他的路由进行连接
- 获取到3xx的重定向要求进行重定向请求,服务器或者代理要求认证信息
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
var request = chain.request
val call = realChain.call
var followUpCount = 0
var priorResponse: Response? = null
var newExchangeFinder = true
var recoveredFailures = listOf<IOException>()
//设置一个死循环,对上面两个可能的情况进行不断的重新发送请求
while (true) {
call.enterNetworkInterceptorExchange(request, newExchangeFinder)
var response: Response
var closeActiveExchange = true
try {
if (call.isCanceled()) {
throw IOException("Canceled")
}
try {
//链式调用
response = realChain.proceed(request)
newExchangeFinder = true
} catch (e: RouteException) {
//抛出路由异常-->是否满足重试条件
if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
throw e.firstConnectException.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e.firstConnectException
}
newExchangeFinder = false
continue
} catch (e: IOException) {
//抛出IO异常-->是否满足重试条件
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newExchangeFinder = false
continue
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
val exchange = call.interceptorScopedExchange
//调用followUpRequest()进行跟进处理,这里面根据服务器返回码进行相应的处理,具体的要翻看http协议返回码对应的意义
val followUp = followUpRequest(response, exchange)
//根据followUp的结果判断是否需要重新发送请求或者直接返回response
if (followUp == null) {
if (exchange != null && exchange.isDuplex) {
call.timeoutEarlyExit()
}
closeActiveExchange = false
return response
}
val followUpBody = followUp.body
if (followUpBody != null && followUpBody.isOneShot()) {
closeActiveExchange = false
return response
}
response.body?.closeQuietly()
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
request = followUp
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
}
(recover)是否满足重试条件的判定逻辑如下:
private fun recover(
e: IOException,
call: RealCall,
userRequest: Request,
requestSendStarted: Boolean
): Boolean {
// 设置重试的参数为false
if (!client.retryOnConnectionFailure) return false
// 请求的body已经发出
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
// 特殊异常类型:ProtocolException,SSLHandshakeException等
if (!isRecoverable(e, requestSendStarted)) return false
//没有更多的route(包含proxy和inetaddress)
if (!call.retryAfterFailure()) return false
// 否则重试当前请求
return true
}
2.4 Interceptors和networkInterceptors
在OkHttpClient.Builder的构造方法有两个参数,使用者可以通过addInterceptor 和 addNetworkdInterceptor 添加自定义的拦截器
从前面添加拦截器的顺序可以知道 Interceptors 和 networkInterceptors 刚好一个在 RetryAndFollowUpInterceptor 的前面,一个在后面。
结合前面的责任链调用图可以分析出来,假如一个请求在 RetryAndFollowUpInterceptor 这个拦截器内部重试或者重定向了 N 次,那么其内部嵌套的所有拦截器也会被调用N次,同样 networkInterceptors 自定义的拦截器也会被调用 N 次。而相对的 Interceptors 则一个请求只会调用一次,所以在OkHttp的内部也将其称之为 Application Interceptor。
Interceptors(应用拦截器)
-
不需要担心中间响应,如重定向和重试。
-
总是调用一次,即使从缓存提供HTTP响应。
-
遵守应用程序的原始意图。不注意OkHttp注入的头像If-None-Match。
-
允许短路和不通话Chain.proceed()。
-
允许重试并进行多次呼叫Chain.proceed()。
networkInterceptors(网络拦截器)
-
能够对重定向和重试等中间响应进行操作。
-
不调用缓存的响应来短路网络。
-
观察数据,就像通过网络传输一样。
-
访问Connection该请求。
2.3 BridgeInterceptor
BridgeInterceptor:封装request和response拦截器
负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应,是从应用程序代码到网络代码的桥梁
BridgeInterceptor拦截器的逻辑流程如下:
- 设置内容长度,内容编码
- 设置gzip压缩,并在接收到内容后进行解压。省去了应用层处理数据解压的麻烦
- 添加cookie
- 设置其他报头,如User-Agent,Host,Keep-alive等。其中Keep-Alive是实现连接复用的必要步骤
class BridgeInterceptor(