敞开成长之旅!这是我参与「日新计划 12 月更文挑战」的第38天,点击查看活动详情
昨天对OkHttp(4.10.0)别离进行了剖析:
OkHttp源码剖析(一)
OkHttp源码剖析(二)
OkHttp源码剖析(三)
OkHttp源码剖析(四)
本篇内容是上面四篇的合集,我个人觉得仍是这样的合集更完好一些,学习起来趁热打铁,所以仍是上传一下完好内容。
源码的OkHttp版别为4.10.0
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
1.简略运用
- okHttp的简略运用代码如下:
//创立OkHttpClient目标
val client = OkHttpClient().newBuilder().build()
//创立Request目标
val request = Request.Builder()
.url("https://wanandroid.com/wxarticle/list/408/1/json") //增加恳求的url地址
.build() //回来一个Request目标
//建议恳求
fun request() {
val response = client
.newCall(request) //创立一个Call目标
.enqueue(object : Callback { //调用enqueue办法履行异步恳求
override fun onFailure(call: Call, e: IOException) {
TODO("Not yet implemented")
}
override fun onResponse(call: Call, response: Response) {
TODO("Not yet implemented")
}
})
}
- 工作流程有四步:
- 创立OkHttpClient目标
- 创立Request目标
- 创立Call目标
- 开端建议恳求,
enqueue
为异步恳求,execute
为同步恳求
2.OkHttpClient目标是怎么创立的
val client = OkHttpClient().newBuilder().build()
OkHttpClient
目标的创立是一个典型的制作者形式,先看一下newBuilder
办法做了什么,源码如下:
//创立了一个Builder目标
open fun newBuilder(): Builder = Builder(this)
class Builder constructor() {
//调度器
internal var dispatcher: Dispatcher = Dispatcher()
//拦截器调集
internal val interceptors: MutableList<Interceptor> = mutableListOf()
//网络拦截器调集
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
...
}
newBuilder
中创立了一个Builder
目标,Builder
目标的结构函数中界说了很多的变量,这儿只保存了3个重要的。
下面看一下build
办法做了什么
//这个this便是上面创立的Builder目标
fun build(): OkHttpClient = OkHttpClient(this)
okHttpClient
源码如下
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
@get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher
@get:JvmName("interceptors") val interceptors: List<Interceptor> =
builder.interceptors.toImmutableList()
@get:JvmName("networkInterceptors") val networkInterceptors: List<Interceptor> =
builder.networkInterceptors.toImmutableList()
...
}
经过OkHttpClient
目标的源码能够得知,Builder
创立的调度器、拦截器终究都会交给OkHttpClient
,这是制作者形式的特定。
3.Request目标是怎么创立的
val request = Request.Builder()
.url("https://wanandroid.com/wxarticle/list/408/1/json") //增加恳求的url地址
.build() //回来一个Request目标
open class Builder {
//恳求地址
internal var url: HttpUrl? = null
//恳求办法
internal var method: String
//恳求头
internal var headers: Headers.Builder
//恳求体
internal var body: RequestBody? = null
...
}
4.创立Call目标
val call = client.newCall(request)
override fun newCall(request: Request): Call {
//newRealCall中传递了三个参数,第一个参数是OkHttpClient本身,第二个参数request,
//第三个不必关注
return RealCall.newRealCall(this, request, forWebSocket = false)
}
接着我们先来看一下RealCall
是什么
class RealCall(
val client: OkHttpClient,
/** The application's original request unadulterated by redirects or auth headers. */
val originalRequest: Request,
val forWebSocket: Boolean
) : Call {
...
}
从源码可知RealCall
是Call
的子类,那么Call
又是什么呢,往下看
//调用是预备好要履行的恳求。也能够撤销调用。
//由于该目标表明单个恳求/呼应对(流),因此不能履行两次。
interface Call : Cloneable {
//回来建议此调用的原始恳求。
fun request(): Request
@Throws(IOException::class)
//同步恳求,当即调用恳求,并堵塞,直到呼应能够处理或呈现过错。
fun execute(): Response
//异步恳求,接受回调参数
fun enqueue(responseCallback: Callback)
//撤销恳求
fun cancel()
//假如此调用已被履行或进入行列,则回来true。屡次履行调用是过错的。
fun isExecuted(): Boolean
//是否是撤销状况
fun isCanceled(): Boolean
//超时时刻,
fun timeout(): Timeout
//创立一个与此调用相同的新调用,即便该调用现已进入行列或履行,该调用也能够被参加行列或履行。
public override fun clone(): Call
fun interface Factory {
fun newCall(request: Request): Call
}
}
5.建议恳求
- 以异步恳求为例进行剖析
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
println("onFailure:$e")
}
override fun onResponse(call: Call, response: Response) {
println("onResponse:${response.body.toString()}")
}
})
RealCall
是Call
的子类所以enqueue
的具体完结是在RealCall
中
override fun enqueue(responseCallback: Callback) {
//查看是否进行了二次恳求
check(executed.compareAndSet(false, true)) { "Already Executed" }
//恳求后当即调用,相当于监听恳求的开端事件
callStart()
//将恳求交给调度器来决议什么时候开端恳求
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
private fun callStart() {
this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
eventListener.callStart(this)
}
- 疑问:
client.dispatcher.enqueue
是怎么决议什么时候开端恳求的- 已知
client
便是OkHttpClient
-
dispatcher
是调度器,先来看一下它的源码
- 已知
class Dispatcher constructor() {
//并发履行的最大恳求数。上面的恳求行列在内存中,等待正在运转的调用完结。
//假如在调用这个函数时有超越maxRequests的恳求在运转,那么这些恳求将坚持在运转状况。
@get:Synchronized var maxRequests = 64
set(maxRequests) {
require(maxRequests >= 1) { "max < 1: $maxRequests" }
synchronized(this) {
field = maxRequests
}
promoteAndExecute()
}
//每台主机可并发履行的最大恳求数。这约束了URL主机名的恳求。
//留意,对单个IP地址的并发恳求依然或许超越此约束:
//多个主机名或许同享一个IP地址或经过同一个HTTP代理路由。
@get:Synchronized var maxRequestsPerHost = 5
set(maxRequestsPerHost) {
require(maxRequestsPerHost >= 1) { "max < 1: $maxRequestsPerHost" }
synchronized(this) {
field = maxRequestsPerHost
}
promoteAndExecute()
}
//线程安全的单例形式,线程池的获取用于线程调度。
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
//界说预备发送的行列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
//界说异步发送行列
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
//界说同步发送行列
private val runningSyncCalls = ArrayDeque<RealCall>()
...
}
- 再来看一下
dispatcher.enqueue
的源码
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
//将call目标增加到预备发送的行列,这个call目标来自AsyncCall,稍后再讲
readyAsyncCalls.add(call)
//修正AsyncCall,使其同享对同一主机的现有运转调用的AtomicInteger。
if (!call.get().forWebSocket) {
//同享一个现已存在的正在运转调用的AtomicInteger
val existingCall = findExistingCallWithHost(call.host())
//统计发送数量
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
//预备发送恳求
promoteAndExecute()
}
//将契合条件的call从readyAsyncCalls(预备发送的行列)增加到runningAsyncCalls(异步发送行列)中
//并在服务器上履行它们
//不能在同步时调用,由于履行调用能够调用用户代码
//假如调度程序当时正在运转,则为true。
private fun promoteAndExecute(): Boolean {
assert(!Thread.holdsLock(this))
//收集一切需求履行的恳求
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
//遍历预备发送的行列
while (i.hasNext()) {
val asyncCall = i.next()
//判别现已发送的恳求是大于等于最大恳求个数64,是则跳出循环
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
//判别并发恳求个数是否大于等于最大并发个数5,假如是则跳出循环
if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue // Host max capacity.
//从预备行列中删去
i.remove()
//计数+1
asyncCall.callsPerHost().incrementAndGet()
executableCalls.add(asyncCall)
//将目标增加到异步发送行列中
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
//提交使命到线程池
asyncCall.executeOn(executorService)
}
return isRunning
}
- 终究恳求是经过
AsyncCall
的executeOn
发送出去的,AsyncCall
是什么
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
...
}
- 接纳了一个回调,继承了Runnable
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
//暂时界说为未履行成功
var success = false
try {
//运用线程履行本身
executorService.execute(this)
//履行成功
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
//发送失利
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
//发送完毕
client.dispatcher.finished(this) // 停止运转
}
}
}
-
AsyncCall
将自己参加到线程池,然后线程池敞开线程履行自己的run办法,那么AsyncCall
参加了一个怎样的线程池呢?
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
- 这儿界说了一个缓存线程池,具有缓存功用且假如一个恳求履行完毕后在60s内再次建议就会复用刚才那个线程,提高了性能。
- 交给线程池后,线程池会敞开线程履行AsyncCall的run办法
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) {
// 不要两次发出回调信号!
Platform.get().log(INFO, "Callback failure for ${toLoggableString()}", 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)
}
}
}
在恳求得到成果后终究会调用finish
表明完结,这儿的finish
又做了什么呢?
/**
* 由[AsyncCall.run]用于表明完结。
*/
internal fun finished(call: AsyncCall) {
call.callsPerHost.decrementAndGet()
finished(runningAsyncCalls, call)
}
/**
* 由[Call.execute]用于表明完结。
*/
internal fun finished(call: RealCall) {
finished(runningSyncCalls, call)
}
private fun <T> finished(calls: Deque<T>, call: T) {
val idleCallback: Runnable?
synchronized(this) {
//将完结的使命从行列中删去
if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
idleCallback = this.idleCallback
}
//用于将等待行列中的恳求移入异步行列,并交由线程池履行
val isRunning = promoteAndExecute()
//假如没有恳求需求履行,回调搁置callback
if (!isRunning && idleCallback != null) {
idleCallback.run()
}
}
-
流程图如下
6.拦截器链
- 上面的代码走到了
getResponseWithInterceptorChain()
,这个办法得到了response
的呼应,先看源码
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
//构建了一个完好的拦截器栈
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 transmitter.noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
getResponseWithInterceptorChain
办法是整个OkHttp完结职责链形式的核心,在这个办法中除了很多拦截器是重点之外还有chain.proceed
这个重点,这儿利用了职责链形式,先看一下这儿面做了什么事情:
override fun proceed(request: Request): Response {
check(index < interceptors.size)
//统计当时拦截器调用proceed办法的次数
calls++
//这儿是对拦截器调用proceed办法的约束,
//在ConnectInterceptor及其之后的拦截器最多只能调用一次proceed!!
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"
}
}
//创立并调用链中的下一个拦截器
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) {
//保证在ConnectInterceptor及其之后的拦截器至少调用一次proceed!!
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
}
上面的proceed
函数中的逻辑就做了一件事,创立下一级职责链并调用下一个拦截器,在getResponseWithInterceptorChain
函数中除CallServerInterceptor
拦截器之外它前面的每一个拦截器都会调用一次proceed
而且只会调用一次,call++
的意图是为了保证职责链能顺次调用。
下面再来聊一聊拦截器,假如没有自界说的拦截器那么就只要5个拦截器,这5个拦截器的功用如下图
1.RetryAndFollowUpInterceptor:
- 重定向或许重试拦截器,一起还创立了一个衔接办理池
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
companion object {
//界说重试或许重定向的次数
//Chrome遵从21个重定向;Firefox, curl和wget跟随20;Safari是16;HTTP/1.0引荐5。
private const val MAX_FOLLOW_UPS = 20
}
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
//chain转化为具体的拦截器链,RealInterceptorChain完结了Interceptor.Chain
val realChain = chain as RealInterceptorChain
var request = chain.request
//这个call便是RealCall目标
val call = realChain.call
var followUpCount = 0
var priorResponse: Response? = null
//不是重试而且能够履行新的路由
var newExchangeFinder = true
var recoveredFailures = listOf<IOException>()
while (true) {
//newExchangeFinder界说为true因此经过enterNetworkInterceptorExchange函数中
//创立了一个新的ExchangeFinder目标,用来办理衔接池
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) {
// 企图与服务器通信失利。恳求或许现已发送。
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newExchangeFinder = false
continue
}
//后边的处理都完毕之后会回传response
//假如存在,请附上从前的response。这样的response是没有主体的。
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
val exchange = call.interceptorScopedExchange
//找出要呼应接纳response的HTTP恳求。
//这将增加身份验证头,遵从重定向或处理客户端恳求超时。
//假如后续操作是不必要的或不适用的,则回来null。
val followUp = followUpRequest(response, exchange)
//获取到的request为空则直接回来response
if (followUp == null) {
if (exchange != null && exchange.isDuplex) {
call.timeoutEarlyExit()
}
closeActiveExchange = false
return response
}
val followUpBody = followUp.body
//request有值且只答应被调用一次则回来response
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若不为空则从头赋值后持续建议恳求
request = followUp
priorResponse = response
} finally {
//假如closeActiveExchange为true则当时交换不被运用且封闭
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
fun enterNetworkInterceptorExchange(request: Request, newExchangeFinder: Boolean) {
check(interceptorScopedExchange == null)
synchronized(this) {
//判别之前的恳求是否是翻开状况,假如是则不能发出新的恳求
check(!responseBodyOpen) {
"cannot make a new request because the previous response is still open: " +
"please call response.close()"
}
check(!requestBodyOpen)
}
if (newExchangeFinder) {
//创立了ExChangeFinder
this.exchangeFinder = ExchangeFinder(
connectionPool,
createAddress(request.url),
this,
eventListener
)
}
}
private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
val route = exchange?.connection?.route()
val responseCode = userResponse.code
val method = userResponse.request.method
when (responseCode) {
HTTP_PROXY_AUTH -> {
//407反常处理
}
HTTP_UNAUTHORIZED -> {
//401反常处理
}
HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
//300 反常处理
//构建重定向恳求
return buildRedirectRequest(userResponse, method)
}
HTTP_CLIENT_TIMEOUT -> {
//408反常处理
}
HTTP_UNAVAILABLE -> {
//503反常处理
}
HTTP_MISDIRECTED_REQUEST -> {
//OkHttp能够兼并HTTP/2衔接,即便域名不同。看到
//RealConnection.isEligible()。假如我们测验这样做,而且服务器回来HTTP 421,
//那么能够测验另一个衔接。
}
}
}
/**
* 构建重定向恳求
*/
private fun buildRedirectRequest(userResponse: Response, method: String): Request? {
// 客户端是否答应重定向?
if (!client.followRedirects) return null
val location = userResponse.header("Location") ?: return null
// 不要遵从重定向到不支撑的协议。
val url = userResponse.request.url.resolve(location) ?: return null
// 假如配置了,不要遵从SSL和非SSL之间的重定向
val sameScheme = url.scheme == userResponse.request.url.scheme
if (!sameScheme && !client.followSslRedirects) return null
// 大多数重定向不包含恳求体。
val requestBuilder = userResponse.request.newBuilder()
if (HttpMethod.permitsRequestBody(method)) {
val responseCode = userResponse.code
val maintainBody = HttpMethod.redirectsWithBody(method) ||
responseCode == StatusLine.HTTP_PERM_REDIRECT ||
responseCode == StatusLine.HTTP_TEMP_REDIRECT
if (HttpMethod.redirectsToGet(method) && responseCode != StatusLine.HTTP_PERM_REDIRECT && responseCode != StatusLine.HTTP_TEMP_REDIRECT) {
requestBuilder.method("GET", null)
} else {
val requestBody = if (maintainBody) userResponse.request.body else null
requestBuilder.method(method, requestBody)
}
if (!maintainBody) {
requestBuilder.removeHeader("Transfer-Encoding")
requestBuilder.removeHeader("Content-Length")
requestBuilder.removeHeader("Content-Type")
}
}
//当跨主机重定向时,删去一切身份验证头。
//这对使用层来说是潜在的麻烦,由于他们没有办法保存它们。
if (!userResponse.request.url.canReuseConnectionFor(url)) {
requestBuilder.removeHeader("Authorization")
}
return requestBuilder.url(url).build()
}
2.BridgeInterceptor:
- 桥接拦截器,担任把用户恳求转化成网络恳求,把网络呼应转化成对用户友好的呼应。
- 源码方面没什么难度便是增加了一些恳求头,需求留意的是Accept-Encoding恳求头,假如用户自己增加了那就需求自己解压,假如用户没有增加则恳求进程会自己增加gizp,一起会主动解压。
3.CacheInterceptor:
- CacheInterceptor便是担任更新和读取缓存的,内部是经过okio进行读取和写入的
class CacheInterceptor(internal val cache: Cache?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
//获取候选缓存
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
//获取缓存战略,强制缓存、比照缓存等
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
if (cacheCandidate != null && cacheResponse == null) {
// 候选缓存不适用,封闭
cacheCandidate.body?.closeQuietly()
}
// 假如网络被制止运用则缓存为空而且抛出反常504
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build().also {
listener.satisfactionFailure(call, it)
}
}
// 假如不需求网络则直接回来缓存即可
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
var networkResponse: Response? = null
try {
//让下一个拦截器处理
networkResponse = chain.proceed(networkRequest)
} finally {
//避免内存走漏
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
//有缓存
if (cacheResponse != null) {
//服务器回来状况码为304则回来缓存成果
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers, networkResponse.headers))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis)
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
networkResponse.body!!.close()
//在兼并头之后更新缓存
cache!!.trackConditionalCacheHit()
cache.update(cacheResponse, response)
return response.also {
listener.cacheHit(call, it)
}
} else {
cacheResponse.body?.closeQuietly()
}
}
//读取网络恳求成果
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
//将恳求成果进行缓存
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
//只记载条件缓存丢掉的日志
listener.cacheMiss(call)
}
}
}
//缓存无效时铲除
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// 无法写入缓存
}
}
}
//回来网络读取的成果
return response
}
}
4.ConnectInterceptor:
- 衔接拦截器便是用来树立衔接的
//树立衔接并持续到下一个拦截程序,网络能够用于回来的呼应,也能够运用GET验证缓存的呼应。
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
//回来一个携带新恳求的exchange目标
//exchange首要作用是发送恳求和接纳呼应的。
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
//让下一层拦截器处理,一起将exchange一起传递曩昔
return connectedChain.proceed(realChain.request)
}
}
-
- 衔接又是怎么树立衔接的
//查找新衔接或池衔接以承载行将到来的恳求和呼应
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
//exchangeFinder是在RetryAndFollowUpInterceptor中创立的
//找到可用的衔接后创立一个新的解码器并回来
val codec = exchangeFinder.find(client, chain)
//创立Exchange目标,首要担任发送恳求和接纳呼应的
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
}
- exchangeFinder.find(client, chain)
fun find(
client: OkHttpClient,
chain: RealInterceptorChain
): ExchangeCodec {
try {
//这儿便是查找可用的链接,假如不可用则一直重复查找,知道找到为止
val resultConnection = findHealthyConnection(
connectTimeout = chain.connectTimeoutMillis,
readTimeout = chain.readTimeoutMillis,
writeTimeout = chain.writeTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
connectionRetryEnabled = client.retryOnConnectionFailure,
doExtensiveHealthChecks = chain.request.method != "GET"
)
//创立并回来一个新的编码器
return resultConnection.newCodec(client, chain)
} catch (e: RouteException) {
trackFailure(e.lastConnectException)
throw e
} catch (e: IOException) {
trackFailure(e)
throw RouteException(e)
}
}
- exchangeFinder.findHealthyConnection
@Throws(IOException::class)
private fun findHealthyConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean,
doExtensiveHealthChecks: Boolean
): RealConnection {
while (true) {
//查找衔接,假如现有衔接存在,则优先选择现有衔接,然后是池,终究构建一个新衔接。
val candidate = findConnection(
connectTimeout = connectTimeout,
readTimeout = readTimeout,
writeTimeout = writeTimeout,
pingIntervalMillis = pingIntervalMillis,
connectionRetryEnabled = connectionRetryEnabled
)
//确认找到的衔接是否能够正常运用
if (candidate.isHealthy(doExtensiveHealthChecks)) {
return candidate
}
//假如衔接不可用就从衔接池中取出
candidate.noNewExchanges()
//这儿便是保证衔接可用,假如前面找到的衔接不可用那么持续查找
if (nextRouteToTry != null) continue
val routesLeft = routeSelection?.hasNext() ?: true
if (routesLeft) continue
val routesSelectionLeft = routeSelector?.hasNext() ?: true
if (routesSelectionLeft) continue
throw IOException("exhausted all routes")
}
}
- exchangeFinder.findConnection
@Throws(IOException::class)
private fun findConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean
): RealConnection {
//测验重用调用中的衔接。
val callConnection = call.connection
if (callConnection != null) {
var toClose: Socket? = null
synchronized(callConnection) {
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
}
//假如衔接还没有被开释则重用
if (call.connection != null) {
check(toClose == null)
return callConnection
}
//衔接被开释
toClose?.closeQuietly()
//从衔接的分配列表中删去
eventListener.connectionReleased(call, callConnection)
}
//需求一个新的衔接。给它新的数据。
refusedStreamCount = 0
connectionShutdownCount = 0
otherFailureCount = 0
//从衔接池获取
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
//衔接池中什么都没有的状况下走这儿
val routes: List<Route>?
val route: Route
if (nextRouteToTry != null) {
//运用来自前一个兼并衔接的路由
routes = null
route = nextRouteToTry!!
nextRouteToTry = null
} else if (routeSelection != null && routeSelection!!.hasNext()) {
// 在现有路由中选择
routes = null
route = routeSelection!!.next()
} else {
//计算出一个新的衔接,这是一个堵塞操作
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
this.routeSelector = localRouteSelector
}
val localRouteSelection = localRouteSelector.next()
routeSelection = localRouteSelection
routes = localRouteSelection.routes
if (call.isCanceled()) throw IOException("Canceled")
//这儿有了一组新的IP地址,所以再次测验从衔接池中获取
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
route = localRouteSelection.next()
}
//创立一个新的衔接
val newConnection = RealConnection(connectionPool, route)
call.connectionToCancel = newConnection
try {
//这儿衔接了服务器
newConnection.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
} finally {
call.connectionToCancel = null
}
call.client.routeDatabase.connected(newConnection.route())
if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
val result = call.connection!!
nextRouteToTry = route
newConnection.socket().closeQuietly()
eventListener.connectionAcquired(call, result)
return result
}
synchronized(newConnection) {
//增加到衔接池中
connectionPool.put(newConnection)
call.acquireConnectionNoEvents(newConnection)
}
eventListener.connectionAcquired(call, newConnection)
return newConnection
}
- 从
exchangeFinder.find
开端到exchangeFinder.findConnection
只做了一件事,便是先测验重用衔接,假如不能重用就从衔接池中取出一个新的衔接,假如无法取出一个衔接就创立一个新的衔接并增加到衔接池中。 - 服务器是怎么衔接的,看这行代码
newConnection.connect
fun connect(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean,
call: Call,
eventListener: EventListener
) {
...
while (true) {
try {
if (route.requiresTunnel()) {
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
if (rawSocket == null) {
//无法衔接
break
}
} else {
//在socket上构建完好HTTP或HTTPS衔接
connectSocket(connectTimeout, readTimeout, call, eventListener)
}
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)
break
} catch (e: IOException) {
...
}
...
}
-
connect
中终究调用的是connectSocket
办法
@Throws(IOException::class)
private fun connectSocket(
connectTimeout: Int,
readTimeout: Int,
call: Call,
eventListener: EventListener
) {
val proxy = route.proxy
val address = route.address
//创立socket目标
val rawSocket = when (proxy.type()) {
Proxy.Type.DIRECT, Proxy.Type.HTTP -> address.socketFactory.createSocket()!!
else -> Socket(proxy)
}
this.rawSocket = rawSocket
eventListener.connectStart(call, route.socketAddress, proxy)
rawSocket.soTimeout = readTimeout
try {
//进行socket衔接
Platform.get().connectSocket(rawSocket, route.socketAddress, connectTimeout)
} catch (e: ConnectException) {
throw ConnectException("Failed to connect to ${route.socketAddress}").apply {
initCause(e)
}
}
try {
//okio的接口,用于输入,相似InputStream
source = rawSocket.source().buffer()
//okio的接口,用于输出,相似OutputStream
sink = rawSocket.sink().buffer()
} catch (npe: NullPointerException) {
if (npe.message == NPE_THROW_WITH_NULL) {
throw IOException(npe)
}
}
}
- 这个办法便是创立了一个
socket
目标然后运用socket
树立衔接,利用okio
的输入输出接口获取输入/输出流
5.CallServerInterceptor:
- 恳求拦截器,在前置预备工作完结后,真实建议了网络恳求。
- 先写入恳求头,假如是GET恳求则恳求完毕,假如是POST恳求则等待回来状况码为100时在发送恳求体
- 然后读取呼应头,经过判别后(非空呼应体)再读取呼应体
- 拿到终究成果后一层层的向上传递,会经过之前的每一个拦截器终究回到自己界说的回调中
6.拦截器的总结:
- 首先经过
RetryAndFollowUpIntercept
拦截器,它首要担任重试和重定向,而且重试或许重定向的次数不能大于20次,一起它还创立了一个ExchangeFinder
目标用于办理衔接池为后续的衔接做预备; - 第二个拦截器是
BridgeInterceptor
拦截器,这个拦截器首要是弥补了恳求头,把用户恳求转化成网络恳求,网络呼应转化成用户能够接纳的呼应,一起还需求留意的一点是假如用户手动增加了Accept-Encoding
那就需求处理解压操作; - 第三个拦截器是
CacheInterceptor
拦截器,首要作用便是缓存response
而且它的内部是经过okio
来处理缓存的; - 第四个拦截器是
ConnectInterceptor
拦截器,这个拦截器是担任树立衔接的,从代码中可知终究是经过RealConnection
目标树立socket
衔接的而且获得了输入输出流为下一步读写做预备,RealConnection
目标的获取时优先复用的,假如无法复用则从衔接池中获取,假如无法获取则创立一个新的衔接并将其放入衔接池中; - 第五个拦截器是
CallServerInterceptor
拦截器,它真实建议了网络恳求,担任数据的读取和写入。
7.OkHttp的缓存战略
OkHttp的缓存是依据HTTP网络协议的,所以这儿需求先来来了解一下HTTP的缓存战略。HTTP的缓存战略是依据恳求和呼应头来标识缓存是否可用,缓存是否可用则是依据有用性和有用期的。
在HTTP1.0时代缓存标识是依据Expires头来决议的,它用来表明绝对时刻,例如:Expires:Thu,31 Dec 2020 23:59:59 GMT,当客户端再次建议恳求时会将当时时刻与上次恳求的时刻Expires进行比照,比照成果表明缓存是否有用可是这种方法存在一定问题,比如说客户端修正了它本地的时刻,这样比照成果就会呈现问题。这个问题在HTTP1.1进行了改进,在HTTP1.1中引入了Cache-Control标识用来表明缓存状况,而且它的优先级高于Expires。Cache-Control常见的取值为下面的一个或许多个:
- private:默许值,用于标识一些私有的业务数据,只要客户端能够缓存;
- public:用于标识通用的业务数据,客户端和服务端都能够缓存;
- max-age-xx:缓存有用期,单位为秒;
- no-cache:需求运用比照缓存验证缓存有用性;
- no-store:一切内容都不缓存,强制缓存、比照缓存都不会触发。
HTTP的缓存分为强制缓存和比照缓存两种:
- 强制缓存:当客户端需求数据时先从缓存中查找是否有数据,假如有则回来没有则向服务器恳求,拿到呼应成果后将成果存进缓存中,而强制缓存最大的问题便是数据更新不及时,当服务器数据有了更新时,假如缓存有用期还没有完毕而且客户端主动恳求时没有增加no-store头那么客户端是无法获取到最新数据的。
- 比照缓存:由服务器决议是否运用缓存,客户端第一次恳求时服务端会回来标识(Last-Modified/If-Modified-Since与ETag/If-None-Match)与数据,客户端将这两个值都存入缓存中,当客户端向服务器恳求数据时会把缓存的这两个数据都提交到服务端,服务器依据标识决议回来200仍是304,回来200则表明需求从头恳求获取数据,回来304表明能够直接运用缓存中的数据
- Last-Modified:客户端第一次恳求时服务端回来的上次资源修正的时刻,单位为秒;
- If-Modified-Since:客户端第再次恳求时将服务器回来的Last-Modified的值放入If-Modified-Since头传递给服务器,服务器收到后判别缓存是否有用;
- ETag:这是一种优先级高于Last-Modified的标识,回来的是一个资源文件的标识码,客户端第一次恳求时将其回来,生成的方法由服务器决议,决议因素包含文件修正的时刻,问津的巨细,文件的编号等等;
- If-None-Match:客户端再次恳求时会将ETag的资源文件标识码放入Header提交。
比照缓存提供了两种标识,那么有什么区别呢:
- Last-Modified的单位是秒,假如某些文件在一秒内被修正则并不能准确的标识修正时刻;
- 资源的修正依据不应该只用时刻来表明,由于有些数据只是时刻有了变化内容并没有变化。
- OkHttp的缓存战略便是依照HTTP的方法完结的,
okio
终究完结了输入输出流,OKHttp的缓存是依据服务器端的Header主动完结的,敞开缓存需求在OkHttpClient创立时设置一个Cache目标,并指定缓存目录和缓存巨细,缓存系统内部运用LRU作为缓存的淘汰算法。下面的代码便是对HTTP的比照缓存和强制缓存的一种完结:- 拿到呼应头后依据头信息决议是否进行缓存;
- 获取数据时判别缓存是否有用,比照缓存后决议是否发出恳求仍是获取本地缓存;
//给定一个恳求和缓存的呼应,它将确定是运用网络、缓存仍是两者都运用。
class CacheStrategy internal constructor(
//发送的网络恳求
val networkRequest: Request?,
//缓存呼应,依据DiskLruCache完结的文件缓存,key为恳求的url的MD5值,value为文件中查询到的缓存
//假如调用不运用缓存,则为null
val cacheResponse: Response?
) {
init {
//这儿初始化,依据传递的cacheResponse判别是否缓存
if (cacheResponse != null) {
this.sentRequestMillis = cacheResponse.sentRequestAtMillis
this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis
val headers = cacheResponse.headers
for (i in 0 until headers.size) {
val fieldName = headers.name(i)
val value = headers.value(i)
when {
fieldName.equals("Date", ignoreCase = true) -> {
servedDate = value.toHttpDateOrNull()
servedDateString = value
}
fieldName.equals("Expires", ignoreCase = true) -> {
expires = value.toHttpDateOrNull()
}
fieldName.equals("Last-Modified", ignoreCase = true) -> {
lastModified = value.toHttpDateOrNull()
lastModifiedString = value
}
fieldName.equals("ETag", ignoreCase = true) -> {
etag = value
}
fieldName.equals("Age", ignoreCase = true) -> {
ageSeconds = value.toNonNegativeInt(-1)
}
}
}
}
}
fun compute(): CacheStrategy {
val candidate = computeCandidate()
//被制止运用网络而且缓存缺乏,
if (candidate.networkRequest != null && request.cacheControl.onlyIfCached) {
//回来networkRequest=null和cacheResponse=null的CacheStrategy
//在缓存拦截器中终究会抛出504的反常
return CacheStrategy(null, null)
}
return candidate
}
/**
* 假设恳求能够运用网络,则回来要运用的战略
*/
private fun computeCandidate(): CacheStrategy {
// 无缓存的呼应
if (cacheResponse == null) {
return CacheStrategy(request, null)
}
// 假如缓存的呼应短少必要的握手,则丢掉它。
if (request.isHttps && cacheResponse.handshake == null) {
return CacheStrategy(request, null)
}
// 假如不应该存储此呼应,则绝不应该将其用作呼应源。
// 只要持久性存储是良好的而且规则是不变的那么这个查看便是剩余的
if (!isCacheable(cacheResponse, request)) {
return CacheStrategy(request, null)
}
val requestCaching = request.cacheControl
//没有缓存 || 假如恳求包含条件,使服务器不必发送客户机在本地的呼应,则回来true
if (requestCaching.noCache || hasConditions(request)) {
return CacheStrategy(request, null)
}
//回来cacheResponse的缓存控制指令。即便这个呼应不包含Cache-Control头,它也不会为空。
val responseCaching = cacheResponse.cacheControl
//回来response的有用期,以毫秒为单位。
val ageMillis = cacheResponseAge()
//获取从送达时刻开端回来呼应刷新的毫秒数
var freshMillis = computeFreshnessLifetime()
if (requestCaching.maxAgeSeconds != -1) {
//从最新呼应的持续时刻和呼应后的服务期限的持续时刻中取出最小值
freshMillis = minOf(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds.toLong()))
}
var minFreshMillis: Long = 0
if (requestCaching.minFreshSeconds != -1) {
//从requestCaching中获取最新的时刻并转为毫秒
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds.toLong())
}
var maxStaleMillis: Long = 0
if (!responseCaching.mustRevalidate && requestCaching.maxStaleSeconds != -1) {
//从requestCaching中获取过期的时刻并转为毫秒
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds.toLong())
}
//
if (!responseCaching.noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
val builder = cacheResponse.newBuilder()
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection "Response is stale"")
}
val oneDayMillis = 24 * 60 * 60 * 1000L
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection "Heuristic expiration"")
}
return CacheStrategy(null, builder.build())
}
// 找到要增加到恳求中的条件。假如满足条件,则不传输呼应体。
val conditionName: String
val conditionValue: String?
when {
etag != null -> {
conditionName = "If-None-Match"
conditionValue = etag
}
lastModified != null -> {
conditionName = "If-Modified-Since"
conditionValue = lastModifiedString
}
servedDate != null -> {
conditionName = "If-Modified-Since"
conditionValue = servedDateString
}
else -> return CacheStrategy(request, null)
}
val conditionalRequestHeaders = request.headers.newBuilder()
conditionalRequestHeaders.addLenient(conditionName, conditionValue!!)
val conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build()
return CacheStrategy(conditionalRequest, cacheResponse)
}
/**
* 从送达日期开端回来呼应刷新的毫秒数
*/
private fun computeFreshnessLifetime(): Long {
val responseCaching = cacheResponse!!.cacheControl
if (responseCaching.maxAgeSeconds != -1) {
return SECONDS.toMillis(responseCaching.maxAgeSeconds.toLong())
}
val expires = this.expires
if (expires != null) {
val servedMillis = servedDate?.time ?: receivedResponseMillis
val delta = expires.time - servedMillis
return if (delta > 0L) delta else 0L
}
if (lastModified != null && cacheResponse.request.url.query == null) {
//正如HTTP RFC所引荐并在Firefox中完结的那样,
//文件的最大过期时刻应该被默许为文件被送达时过期时刻的10%
//默许过期日期不必于包含查询的uri。
val servedMillis = servedDate?.time ?: sentRequestMillis
val delta = servedMillis - lastModified!!.time
return if (delta > 0L) delta / 10 else 0L
}
return 0L
}
/**
* 回来response的有用期,以毫秒为单位。
*/
private fun cacheResponseAge(): Long {
val servedDate = this.servedDate
val apparentReceivedAge = if (servedDate != null) {
maxOf(0, receivedResponseMillis - servedDate.time)
} else {
0
}
val receivedAge = if (ageSeconds != -1) {
maxOf(apparentReceivedAge, SECONDS.toMillis(ageSeconds.toLong()))
} else {
apparentReceivedAge
}
val responseDuration = receivedResponseMillis - sentRequestMillis
val residentDuration = nowMillis - receivedResponseMillis
return receivedAge + responseDuration + residentDuration
}
...
}
- OkHttp缓存的恳求只要GET,其他的恳求方法也不是不能够可是收益很低,这本质上是由各个method的运用场景决议的。
internal fun put(response: Response): CacheRequest? {
val requestMethod = response.request.method
if (HttpMethod.invalidatesCache(response.request.method)) {
try {
remove(response.request)
} catch (_: IOException) {
// 无法写入缓存。
}
return null
}
if (requestMethod != "GET") {
// 不要缓存非get呼应。技术上我们答应缓存HEAD恳求和一些
//POST恳求,可是这样做的复杂性很高,收益很低。
return null
}
...
}
- 完好流程图如下
8.OkHttp的衔接池
/**
* 办理HTTP和HTTP/2衔接的重用,以减少网络延迟。同享相同地址的HTTP恳求能够同享一个衔接。
*/
class ConnectionPool internal constructor(
internal val delegate: RealConnectionPool
) {
constructor(
maxIdleConnections: Int,
keepAliveDuration: Long,
timeUnit: TimeUnit
) : this(RealConnectionPool(
taskRunner = TaskRunner.INSTANCE,
maxIdleConnections = maxIdleConnections,
keepAliveDuration = keepAliveDuration,
timeUnit = timeUnit
))
constructor() : this(5, 5, TimeUnit.MINUTES)
/** 回来池中闲暇衔接的数量。 */
fun idleConnectionCount(): Int = delegate.idleConnectionCount()
/** 回来池中的衔接总数。 */
fun connectionCount(): Int = delegate.connectionCount()
/** 封闭并删去池中的一切闲暇衔接。 */
fun evictAll() {
delegate.evictAll()
}
}
- OkHttp支撑5可并发KeepLive,默许链路生命为5分钟,衔接池的终究完结是
RealConectionPool
,增加衔接到衔接池的代码如下
//RealConnectionPool#put
fun put(connection: RealConnection) {
connection.assertThreadHoldsLock()
//增加到衔接池
connections.add(connection)
//经过铲除行列的schedule,铲除闲暇时刻最长的衔接超越坚持衔接约束或闲暇衔接约束的衔接
cleanupQueue.schedule(cleanupTask)
}
9.OkHttp的闲暇衔接怎么铲除
private val cleanupTask = object : Task("$okHttpName ConnectionPool") {
override fun runOnce() = cleanup(System.nanoTime())
}
/**
* 在此池上履行维护,假如闲暇时刻最长的衔接超越坚持衔接约束或闲暇衔接约束,则删去该衔接。
* 回来休眠的持续时刻(以纳秒为单位),直到下一次预订调用此办法。假如不需求进一步整理,则回来-1。
*/
fun cleanup(now: Long): Long {
//正在运用的衔接数量
var inUseConnectionCount = 0
//闲暇的衔接数量
var idleConnectionCount = 0
//最长闲暇衔接
var longestIdleConnection: RealConnection? = null
//最长闲暇时刻
var longestIdleDurationNs = Long.MIN_VALUE
// 找到要整理的衔接,或许找到下一次整理的截止时刻。
for (connection in connections) {
synchronized(connection) {
// 假如衔接正在运用,持续查找
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++
} else {
idleConnectionCount++
// 假如衔接现已预备好被整理,我们就完结了。
//闲暇时刻 = 当时时刻 - 搁置时刻
val idleDurationNs = now - connection.idleAtNs
//假如闲暇时刻 > 最长闲暇时刻
if (idleDurationNs > longestIdleDurationNs) {
//闲暇时刻赋值给最长闲暇时刻
longestIdleDurationNs = idleDurationNs
//当时connection赋值给最长闲暇衔接
longestIdleConnection = connection
} else {
Unit
}
}
}
}
when {
//最长闲暇时刻 >= 存活时刻 || 闲暇的衔接数量 > 最大闲暇衔接数量
longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections -> {
// 选择了一个前面查找到的衔接来整理。整理前再次确认它是否能够被整理,然后封闭
val connection = longestIdleConnection!!
synchronized(connection) {
if (connection.calls.isNotEmpty()) return 0L // 不再闲暇
if (connection.idleAtNs + longestIdleDurationNs != now) return 0L // 不再闲暇
connection.noNewExchanges = true
//铲除衔接
connections.remove(longestIdleConnection)
}
connection.socket().closeQuietly()
if (connections.isEmpty()) cleanupQueue.cancelAll()
// 再次整理。
return 0L
}
// 闲暇衔接数量 > 0
idleConnectionCount > 0 -> {
// 衔接行将被铲除。
// 回来 存活时刻 - 最长闲暇时刻
return keepAliveDurationNs - longestIdleDurationNs
}
// 正在运用的衔接数量 > 0
inUseConnectionCount > 0 -> {
// 一切衔接都在运用中。在我们再次运转之前,它至少是坚持存活的时刻。
// 回来衔接的存活时刻
return keepAliveDurationNs
}
else -> {
//没有衔接,闲暇或正在运用。
return -1
}
}
}
闲暇衔接的铲除具体分为2个步骤:
- 找到当时衔接是闲暇的而且这个衔接的闲暇时刻大于设定的最长闲暇时刻的衔接,只要这种衔接才能够被铲除;
- 条件判别:最长闲暇时刻 >= 存活时刻 || 闲暇的衔接数量 > 最大闲暇衔接数量 ,契合这个条件后才能够经过
remove
铲除衔接,而且在铲除前还要进行确认是闲暇的衔接;
两个特点值的获取:
- 判别是否有闲暇衔接,假如有则回来闲暇衔接的闲暇时刻;
- 判别是否有正在运用的衔接,如固有则回来衔接的存活时刻;
10.Application Interceptors与Network Interceptors
- Application Interceptors是使用拦截器,Network Interceptors是网络拦截器;
- 使用拦截器是用于在恳求发送前和网络呼应后的拦截器,只能触发一次。而网络拦截器在产生过错重试或许重定向时能够履行屡次,相当于进行了二次恳求;
- 假如CacheInterceptor命中了缓存就不在进行网络恳求了,因此会存在短路网络拦截器的状况;
- 使用拦截器通常用于统计客户端的网络恳求建议状况;网络拦截器中能够获取到终究发送恳求的request也包含重定向的数据,也能够获取真实产生网络恳求的回来的response,然后修正对应的恳求和呼应数据。