http框架--OkHttp 4 架构与源码分析
作者:互联网
1. OkHttp
1.1. 概述
OkHttp现在应该算是最火的Http第三方库,Retrofit底层也是使用OkHttp
OkHttp官网地址:http://square.github.io/okhttp/
OkHttp GitHub地址:https://github.com/square/okhttp
1.2. 出现背景
网络访问的高效性要求,可以说是为高效而生
1.3. 特性
- 提供了对 HTTP/2 的支持,对同一个主机发出的所有请求都可以共享相同的套接字连接
- 如果HTTP/2 不可用,使用连接池来复用连接,减少请求延迟,提高请求效率
- 提供了对 GZIP 的默认支持来降低传输内容的大小
- 提供了对 HTTP 响应的缓存机制,可以避免不必要的网络请求
- 当网络出现问题时,OkHttp 会自动重试一个主机的多个 IP 地址【当网络出现问题时,OkHttp会保持不变:它会从常见的连接问题中静默地恢复。如果您的服务有多个IP地址,OkHttp将尝试替代地址,如果第一次连接失败。这对于IPv4+IPv6和托管在冗余数据中心中的服务是必要的。OkHttp支持现代TLS特性(tls1.3, ALPN,证书固定)。它可以配置为后退以实现广泛的连接】
使用OkHttp很简单。它的请求/响应API具有fluent builders(链接构造)和不变性。它既支持同步阻塞调用,也支持带有回调的异步调用。
2. 架构流程图
2.1. 请求流程图
- 首先创建OkHttpClient对象,OkHttpClient是okhttp框架的客户端,用于发送http请求(Requests)和读取请求返回数据(Responses)
- 在OkHttp4中,每一个请求都被封装为一个Call,底层实现是RealCall。
- 创建Call对象时候传进去了一个Request对象,Request对象表示用户的交易请求参数
- okhttp中提供了两种请求方式:一种是同步请求,第二种是异步请求。
- Dispatcher是okhttp的任务调度核心类,负责管理同步和异步的请求,管理每一个请求任务的请求状态,并且其内部维护了一个线程池用于执行相应的请求
- 同步交易请求 call.execute()
- 异步交易请求 call.enqueue(Callback callback)
- 不管是同步请求还是异步请求,最后都会走getResponseWithInterceptorChain()方法,getResponseWithInterceptorChain()是okhttp中的精髓设计之一
- 在拦截器链中执行的结果,在同步请求中会直接在response返回,而异步请求时会把拦截器链的处理结果通过Callback的onReponse回调给用户。
2.2. 拦截器链
2.2.1. interceptor分类
OkHttp中用户可传入的interceptor分为两类:
- 全局interceptor,该类interceptor在请求开始之前最早被调用
- 为非网页请求的networkInterceptor,这类interceptor只有在非网页请求中会被调用,并且是在组装完成请求之后,真正发起请求之前被调用(这块具体可以参看RealCall#getResponseWithInterceptorChain()方法)
拦截器 | 作用 |
---|---|
用户自定义的拦截器 | 用户实现的一些自定义拦截功能,如记录日志 |
retryAndFollowUpInterceptor | 重试和重定向拦截器,主要负责网络失败重连 |
BridgeInterceptor | 主要是桥接应用层和网络层: 添加必要的请求头信息【encoding,cookie,userAgent等】、gzip处理等 |
CacheInterceptor | 缓存拦截器,主要负责拦截缓存 |
ConnectInterceptor | 网络连接拦截器,主要负责正式开启http请求 |
CallServerInterceptor | 负责发送网络请求和读取网络响应 |
2.2.2. 拦截过程
getResponseWithInterceptorChain()是okhttp中的精髓设计之一。
通过拦截器链对请求数据和返回数据进行处理,内部采用责任链模式,将每一个拦截器对应负责的处理任务进行严格分配,最后将交易结果返回并回调暴露给调用者的接口上,代码如下:
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of 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)
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 {
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)
}
}
整个请求过程通过RealInterceptorChain#proceed来连接,在每个interceptor中调用下一个interceptor来完成整个请求流程,并且在回到当前interceptor后完成响应处理。
在调用拦截器的intercept(next)方法前,会copy当前的RealInterceptorChain【只有index不同】赋值给next变量。
只有当前拦截器的response返回有结果时,才会执行下一个拦截器,因此得出结论:下一个拦截器依赖于当前拦截器的返回,可以保证拦截器的依次执行
@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
}
2.3. 总结
至此,okhttp的整体架构分析完毕,可以跟着源码一步步去理解,去了解okhttp的设计思想,然后应用到项目开发中。
okhttp是一个很庞大的一个框架,本文仅仅从请求流程和拦截器方面做了简单的分析,内部的实现逻辑和思想都很值得认真思考和细细品味
标签:拦截器,http,请求,--,interceptors,okhttp,源码,OkHttp,interceptor 来源: https://blog.csdn.net/penriver/article/details/118355639