OkHttp的历史
- OKHttp最初是基于HttpClient的封装,因为HttpClient或是谷歌的提供的方案过于繁琐
- 后面Square完全移除了HttpClient等底层网络请求原生支持,完全从头开始进行了实现
- 目前成为了公认的业界网络请求方案
OkHttp的简单使用和调用链
val client = OkHttpClient()
val req = Request.Builder().url("https://www.baidu.com").build()
client.newCall(req).enqueue(object : Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
}
})
OkHttpClient如他的名字一样,是一个请求中心负责请求的管理, Request是请求条件,可以传入请求体,header等信息;
我们从newCall(req)这边跟踪进去,会发现我们newCall创建的是RealCall对象
先看RealCall的构造函数,有三个参数
constructor(client: okhttp3.OkHttpClient, originalRequest: okhttp3.Request, forWebSocket: kotlin.Boolean)
client就是我们创建的OkHttpClient
originalRequest即是我们创建的request
forWebSocket这个参数主要是用于WebSocket双向通信的,OkHttp早期版本均一直为flase,最新版本开始支持WebSocket后开始有实际作用
我们来看看这个realCall在调用enqueue()阶段做了些什么
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
这个callStart()和请求本身无关,是添加了一个eventListener,用于跟踪程序的错误,连接断开响应,TCP状态变化等事件
我们继续查看client.dispatcher.enqueue(AsyncCall(responseCallback))
我们先看Dispatcher,Dispatcher是OKHttpClient内的一个对象,用于多线程的管理的工具,
有几个属性
maxRequests:最大请求数量
maxRequestsPerHost:对同一主机最大请求数量
我们接着看其enqueue()阶段做了些什么
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
可见我们的请求,被放入了一个readyAsyncCalls的双向队列当中;
之后对AsyncCall中访问同一主机数量的变量进行更新
再然后调用promoteAndExecute()
从promoteAndExecute()这个方法名可以推举出他的行为:
1.推举出可执行的call
2.将这些call进行执行
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
// 遍历这些Call
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
// 检查是到达最大请求数量和同一主机最大请求数
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
i.remove()
asyncCall.callsPerHost.incrementAndGet()
// 放入被选中的calls队列
executableCalls.add(asyncCall)
// 放入正在被执行的calls队列
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
if (executorService.isShutdown) {
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.callsPerHost.decrementAndGet()
synchronized(this) {
runningAsyncCalls.remove(asyncCall)
}
asyncCall.failRejected()
}
idleCallback?.run()
} else {
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
}
return isRunning
可见最终执行请求走的是asyncCall.executeOn方法
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
failRejected(e)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
executeOn()是在线程池中执行自己的runnable
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
val response = getResponseWithInterceptorChain()
signalledCallback = true
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
client.dispatcher.finished(this)
}
}
}
自此请求结束了,getResponseWithInterceptorChain()获取到了请求结果后,对我们传入的responseCallback进行响应
总结一下请求的调用链
client.newCall(req)这时候创建了一个RealCall- 调用
enqueue(),既是调用RealCall的enqueue() - enqueue中调用了OkHttpClient的dispatcher的
enqueue - dispatcher的
enqueue将calls传入readyAsyncCalls队列中,并执行promoteAndExecute() promoteAndExecute()将符合执行条件的calls进行执行请求- 执行请求本质上是调用call的
executeOn(executorService: ExecutorService),executeOn(executorService: ExecutorService)是用传入的线程池执行内部的runnable接口 - runnable内执行请求,获取返回后响应我们传入的回调
responseCallback
参数介绍
OKHttp将连接,请求获取返回全部业务都集成在了方法getResponseWithInterceptorChain()中,为了更好的了解其中都做了什么,我们先看下OkHttpClient的初始化参数
- dispatcher 【线程调度工具】
- interceptors 【拦截器】
- networkInterceptors 【网络拦截器】
- connectionPool 连接池【负责连接的批量管理,如果要进行连接,会先查看其中有没有可复用的连接,进行复用,没有才进行创建,超过一定时间可以自动进行销毁,可以免去握手过程从而加快请求速度,和节约资源】
- eventListenerFactory【事件监听器】
- retryOnConnectionFailure【自动重试】
- authenticator【认证工具】
- followRedirects【是否根据服务器返回重定向】
- followSslRedirects【当重定向后的地址协议发生了变化,是否依旧重定向】
- cookieJar【cookie罐,提供接口支持给我们存储cookie和加载cookie】
- dns【DNS解析,默认实现是调用Java原生InetAddress.getAllByName()方法根据域名获取ip地址数组,可以自实现】
- cache【缓存】
- proxy【代理,默认使用proxySelector获取到的配置,可以配置代理服务器地址】
- sslSocketFactory【用于创建加密Socket连接】
- socketFactory【用于创建Socket连接】
- x509TrustManager【原生类,证书验证管家】
- connectionSpecs【连接规范,tcp建立连接要向服务器发送支持的协议和加密算法等信息,可以在此自定义】
- protocols【支持的HTTP协议版本号】
- hostnameVerifier【主机名验证器】
- certificatePinner【验证图钉,在验证证书合法逻辑之外可以加Hash验证】
- certificateChainCleaner【清理证书】
- pingInterval【心跳间隔,http2/websocket用】 authenticator使用方式示例:
OkHttpClient().newBuilder()
.authenticator(object : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
// 执行获取最新token业务
...
// 更新请求信息后返回
return response.request.newBuilder()
.header("Authorization", "Bearer ${getToken()}")
.build()
}
})
现在我们回到getResponseWithInterceptorChain()
@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)
}
}
}
工作链的实现
先引入一个概念,链式工作, A->B->C,按照从前往后执行过去,再A<-B<-C执行回来,中间的每个环节有前置工作,有后置工作
getResponseWithInterceptorChain()中创建了interceptors这个链条,并且传入了RealInterceptorChain,这里我们可以把RealInterceptorChain抽象为这个链条本身,interceptors是链条的队列,其他参数为这个链条的整体参数
我们直接从入口进行查看
val response = chain.proceed(originalRequest)
点进去看proceed的实现
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
calls++
if (exchange != null) {
check(exchange.finder.routePlanner.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"
}
}
return response
}
这里调用了链条中的第一环也就是RetryAndFollowUpInterceptor的intercept方法,并且传入了index+1后的参数next
我们点进去看下RetryAndFollowUpInterceptor的intercept方法的具体实现如下
@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 newRoutePlanner = true
var recoveredFailures = listOf<IOException>()
while (true) {
call.enterNetworkInterceptorExchange(request, newRoutePlanner, chain)
var response: Response
var closeActiveExchange = true
try {
if (call.isCanceled()) {
throw IOException("Canceled")
}
try {
response = realChain.proceed(request)
newRoutePlanner = true
} catch (e: IOException) {
// An attempt to communicate with a server failed. The request may have been sent.
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newRoutePlanner = false
continue
}
// Clear out downstream interceptor's additional request headers, cookies, etc.
response =
response.newBuilder()
.request(request)
.priorResponse(priorResponse?.stripBody())
.build()
// 检查是否需要重定向
val exchange = call.interceptorScopedExchange
val followUp = followUpRequest(response, exchange)
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")
}
// 需要重定向 就更新req信息后从头开始
request = followUp
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
我们看见了
response = realChain.proceed(request)
这时候又调用了realChain.proceed(request), 但是这时候index已经加了1,所以这次执行等于执行了下一个环的intercept
所以可以把Interceptor的工作流程抽象如下
override fun intercept(chain: Interceptor.Chain): Response {
// 执行前置工作
....
// 执行中置工作 让下一环节执行前置工作,并向后传递让下下环境执行前置工作和向后传递..
realChain.proceed(request)
// 等待中置工作结束 执行后置工作
}
至此我们可以理解了这条工作链的实现方式
现在我们看看每个Interceptor都做了什么
RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor主要做两个工作
- Retry 重试
- FollowUp 重定向
实现重试的机制也简单
我们简化下结构
while (true) {
...
try {
// 执行请求
} catch (e: IOException) {
// 检查是否符合重试条件,是就 continue,
// 不符合(比如配置不允许重试,404等)就抛出异常
continue
}
}
就是循环中,trycatch捕获异常,如果有异常就执行continue,从头开始,这其中加了些额外环节,比如recover环节就是发生异常后是直接抛出异常,还是继续重试
BridgeInterceptor
这个环节比较简单 主要是给我们添加请求头信息,帮我们添加编码信息,contentLength数据长度等信息
CacheInterceptor
OkHttp的缓存实现
ConnectInterceptor
这个要展开说明下,先看下基础的结构
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(realChain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
}
这个结构比较特殊,没有了后置工作,因为这是请求环节的最后一环。
先看initExchange
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
val connection = exchangeFinder.find()
val codec = connection.newCodec(client, chain)
val result = Exchange(this, eventListener, exchangeFinder, codec)
this.interceptorScopedExchange = result
this.exchange = result
synchronized(this) {
this.requestBodyOpen = true
this.responseBodyOpen = true
}
if (canceled) throw IOException("Canceled")
return result
}
这边将RealInterceptorChain转成了Exchange
codec意思是coder和decoder,编码解码器
这边根据我们请求的格式如HTTP1,HTTP2,给到不同的编码解码器,然后提供给Exchange
未完待续-