AndroidOkHttp源码解读逐字稿-拦截器
Posted Q-CODER
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AndroidOkHttp源码解读逐字稿-拦截器相关的知识,希望对你有一定的参考价值。
目录
第一个 拦截器 RetryAndFollowUpInterceptor
0.前言
作为一个已经工作*年的 android 开发工程师,感觉自己是时候对了解源码的内容了(不管什么时候,阅读源码都是很重要的)。这次基于阅读各方的有关学习资料后,自己花了近8个小时,写出的这篇逐字稿。其中可能还是存在不全面,甚至有错误的地方。望谅解。后面会进行二次,三次,甚至多次的修改。也希望大家多多指正,交流~
本篇文章旨在将 OkHttp 做一个请求的流程,在源码中是如何实现的进行浅析。
1.OkHttp的简单使用
可以直接在 官网 引用 https://square.github.io/okhttp/
以下是一个简单的使用。本文暂时不分析大管家- OkHttpClient
val url = "https://square.github.io/okhttp/"
val client = OkHttpClient()
val request = Request.Builder()
.url(url).build()
try {
client.newCall(request).execute()
} catch (e: Exception) {
Log.i("TAG", "onCreate: Exception---${e.message}")
}
2.浅析开始
首先从最后一行(有意义的一行)入手,最后是调用了 execute()。【那么,我们所有的分析都是为了知道 execute 到底做了什么?】
查看后发现是 接口 Call 的一个方法。那么我们就看看调用 execute() 的类。那就是 . 前面的东西。
查看后 newCall 是属于 OkHttpClient 类,然后这个方法
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
接受了一个 request 参数【这个 request 参数就是我们自己创建的 request】,通过 RealCall 返回一个 Call。 那么我们接着看看 RealCall 是怎么创建一个 Call 的呢?
现在进入到了 RealCall 类
那进来后,发现 RealCall 类实现了 Call 接口。那么回到最开始,我们就是想知道 execute 做了什么?那么我们来看看 RealCall 中的 execute 做了什么吧。(因为 RealCall 不是抽象类,所以exectue 一定有具体实现)
哈哈哈,果不其然。的确有
override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
到这一步,就将问题转化为,RealCall 的 execute 做了什么?
直接看 return 的东西,是一个 getResponseWithInterceptorChain() 。那么我们进去看看这个方法做了什么吧?[ 这个方法还是在 RealCall 中]
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)
}
}
}
到这一步,就将问题转化为,getResponseWithInterceptorChain 做了什么?
依旧先找 return 的东西。发现 return 了个 response,而产生这个 response 是调用了 chain.proceed(originalRequest) 。那么我们看看这里的 proceed() 的方法做了什么吧?
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
}
同样,找最后一行 return。往上追,看到是通过 interceptor.intercept(next) 得到的结果。next 会在上面被+1 重新赋值,这样就可以把接力棒往下传了。
嗯?这个 interceptor 是个啥?哪来的?
【在这里打断一下,反问一下,我们在干嘛?为什么走到这步了?】
哦~原来是我们追溯到 RealCall 的 execute 方法中,然后里面有 getResponseWithInterceptorChain() 然后这个方法中又调用了 chain.proceed(originalRequest),于是我们跳到了 RealInterceptorChain 类中查看 proceed 做了什么。这个时候,我们发现这里面调用了一个 interceptor.intercept(next) 。于是我们好奇了 interceptor 是哪里来的,以及它的 intercept 做了什么?我们知道这两个之后,就知道了 RealCall 的 execute 方法做了什么,然后我们就知道 OkHttp 是如何实现一个请求的。(嗯嗯,心满意足)
首先还是先点击 intercept 的这个方法。发现,他是一个接口,interceptor 接口 的方法。老规矩,我们接着往上看。看看 具体的 interceptor
class RealInterceptorChain(
internal val call: RealCall,
private val interceptors: List<Interceptor>,
private val index: Int,
internal val exchange: Exchange?,
internal val request: Request,
internal val connectTimeoutMillis: Int,
internal val readTimeoutMillis: Int,
internal val writeTimeoutMillis: Int
)
找到根处,发现这个是我们创建 RealInterceptorChain 实例的时候,传进来的。那么我们追回上一层 RealCall 中
哦,就是有一个 list 变量,用来装“一堆” interceptor 的。
拦截器
链式调用流程示意图
那么接下来就尝试把一个个 Interceptor 解读一遍。在解读之前,补充一点整个链式调用流程大概如下图所示
除了最后一个 interceptor 节点,每一个节点都分为三个工作流程,前置,中置,后置。
【中置工作】将流程推到一下个节点。(通过调用 realChain.proceed(request))。
【前置工作】就是本身拦截器要做的一些事情。(例如:寻找可用的链接,拿到可用的 Cache 等等)
【后置工作】就是等下个节点把返回值返回后的一个工作处理。
有了上面的流程基础,我们就可以开始分析了。
第 0 个拦截器
client.interceptors //这个是我们自己定义的 interceptors,我们暂时不理会
第一个 拦截器 RetryAndFollowUpInterceptor
/**
* This interceptor recovers from failures and follows redirects as necessary. It may throw an
* [IOException] if the call was canceled.
*/
刚刚我们好奇的就是这些 interceptor 的 intercep 方法做了什么,所以直接看 intercept 方法即可。
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) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
throw e.firstConnectException.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e.firstConnectException
}
newExchangeFinder = false
continue
} 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
}
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
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")
}
request = followUp
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
有了上面的铺垫(调用流程示意图),那么我们只需要知道 realChain.proceed(request) 就可以划分出 前置,后置的工作了。
[//♠♠♠♠♠♠♠♠♠♠♠♠♠♠]处,就是【中置工作】。所以,我们想要【前置工作】做了什么,就往前面看。
【前置工作】call.enterNetworkInterceptorExchange(request, newExchangeFinder)
【后置工作】简单分析一下,就在尝试和重定向,直至成功后,返回拿到的数据 response,并将其返回。
追溯 enterNetworkInterceptorExchange,发现回到了 RealCall 这个类当中
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) {
this.exchangeFinder = ExchangeFinder(
connectionPool,
createAddress(request.url),
this,
eventListener
)
}
}
其中上面方法中 newExchangeFinder 为 true,所以将执行 if 代码块
这里主要是创建了一个 ExchangeFinder 实例。暂时先不看这个类是什么?因为现在,我们只需要知道他创建了这么一个实例出来,等真正调用了对应的方法的时候,我们再去看对应的方法即可。
第二个拦截器 BridgeInterceptor
/**
* Bridges from application code to network code. First it builds a network request from a user
* request. Then it proceeds to call the network. Finally it builds a user response from the network
* response.
*/
通过名字和代码注释,这个拦截器,主要是负责 application 和 network 连接的一个连接桥拦截器。
override fun intercept(chain: Interceptor.Chain): Response {
val userRequest = chain.request()
val requestBuilder = userRequest.newBuilder()
val body = userRequest.body
if (body != null) {
val contentType = body.contentType()
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString())
}
val contentLength = body.contentLength()
if (contentLength != -1L) {
requestBuilder.header("Content-Length", contentLength.toString())
requestBuilder.removeHeader("Transfer-Encoding")
} else {
requestBuilder.header("Transfer-Encoding", "chunked")
requestBuilder.removeHeader("Content-Length")
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", userRequest.url.toHostHeader())
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive")
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
//♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠
val networkResponse = chain.proceed(requestBuilder.build())
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
val responseBody = networkResponse.body
if (responseBody != null) {
val gzipSource = GzipSource(responseBody.source())
val strippedHeaders = networkResponse.headers.newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build()
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header("Content-Type")
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
return responseBuilder.build()
}
同理,找到 ♠ 所在所在位置。即是分割前置和后置工作的地方。
【前置工作】组装请求头
【后置工作】将数据解压,转成 response,并将其返回
第三个拦截器 CacheInterceptor
/** Serves requests from the cache and writes responses to the cache. */
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) {
// The cache candidate wasn't applicable. Close it.
cacheCandidate.body?.closeQuietly()
}
// If we're forbidden from using the network and the cache is insufficient, fail.
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 we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
if (cacheResponse != null) {
listener.cacheConditionalHit(call, cacheResponse)
} else if (cache != null) {
listener.cacheMiss(call)
}
var networkResponse: Response? = null
try {
//♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠
networkResponse = chain.proceed(networkRequest)
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
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()
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
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)) {
// Offer this request to the cache.
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
// This will log a conditional cache miss only.
listener.cacheMiss(call)
}
}
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
【前置工作】拿到一个可用的 cache
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
【后置工作】cacheWritingResponse 放返回来的 cache 存起来。
第四个拦截器 ConnectInterceptor
/**
* Opens a connection to the target server and proceeds to the next interceptor. The network might
* be used for the returned response, or to validate a cached response with a conditional GET.
*/
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
//♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠♠
return connectedChain.proceed(realChain.request)
}
那么在这上面,
可以看到【中置工作】依旧是 proceed
然后【后置工作】就是将其结果返回。
【前置工作】打开目标服务器的连接。
核心的代码是第 3 行代码。那溯源,发现是在 RealCall 的一个方法
/** Finds a new or pooled connection to carry a forthcoming request and response. */
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
val codec = exchangeFinder.find(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
}
老规矩,看看最后一行, return 了一个 result。往上看,看到第 12 行代码生成的。发现其中传入的一个参数是 exchangeFinder。咦,等等,这个是我们在RetryAndFollowUpInterceptor 这个拦截器生成的那个吗?是的,因为在 10 行,发现了。就是那个。它被用到了,第 11 行,调用了它的 find 方法。我们进去看看
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)
}
}
最后返回的是一个 ExchangeCodec (/** Encodes HTTP requests and decodes HTTP responses. */)
看 return ,看 newCodec()【 RealCall 】 方法。这里不贴代码了。就是根据是否是 HTTP2 来生成 HTT2 或者 HTTP1 的编码解码器。
那 resultConnection 是什么呢?findHealthyConnection() 生成的
溯源,发现这个在 ExchangeFinder 类中 最后发现是调用了 findConnection()
private fun findConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean
): RealConnection {
if (call.isCanceled()) throw IOException("Canceled")
// Attempt to reuse the connection from the call.
val callConnection = call.connection // This may be mutated by releaseConnectionNoEvents()!
if (callConnection != null) {
var toClose: Socket? = null
synchronized(callConnection) {
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
}
// If the call's connection wasn't released, reuse it. We don't call connectionAcquired() here
// because we already acquired it.
if (call.connection != null) {
check(toClose == null)
return callConnection
}
// The call's connection was released.
toClose?.closeQuietly()
eventListener.connectionReleased(call, callConnection)
}
// We need a new connection. Give it fresh stats.
refusedStreamCount = 0
connectionShutdownCount = 0
otherFailureCount = 0
// Attempt to get a connection from the pool.
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
// Nothing in the pool. Figure out what route we'll try next.
val routes: List<Route>?
val route: Route
if (nextRouteToTry != null) {
// Use a route from a preceding coalesced connection.
routes = null
route = nextRouteToTry!!
nextRouteToTry = null
} else if (routeSelection != null && routeSelection!!.hasNext()) {
// Use a route from an existing route selection.
routes = null
route = routeSelection!!.next()
} else {
// Compute a new route selection. This is a blocking operation!
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")
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. We have a better chance of matching thanks to connection coalescing.
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
route = localRouteSelection.next()
}
// Connect. Tell the call about the connecting call so async cancels work.
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 we raced another call connecting to this host, coalesce the connections. This makes for 3
// different lookups in the connection pool!
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
}
搜索 (Ctrl + F)return result,发现有三处有关返回结果。那么我们从第一个开始看起
上面代码的第40行,
这个 if 代码块中,判断条件,看上去就是从一个连接池中到拿到一个连接。如果拿到了就将这个连接返回。
溯源 callAcquirePooledConnection() 在 RealConntectionPool 这个类中
fun callAcquirePooledConnection(
address: Address,
call: RealCall,
routes: List<Route>?,
requireMultiplexed: Boolean
): Boolean {
for (connection in connections) {
synchronized(connection) {
if (requireMultiplexed && !connection.isMultiplexed) return@synchronized
if (!connection.isEligible(address, routes)) return@synchronized
call.acquireConnectionNoEvents(connection)
return true
}
}
return false
}
可以看到,就是从所有的连接中,遍历出一个可以用的连接,如果存在则返回 true,否则反之。
因为调用了三次这个方法,那么我们分析一下对应的参数有什么意义。
address 主要是包含了 host 和 port 的 Address 实例
call 就是 RealCall 的实例
routes 路由列表(可空)
requireMultiplexed 是否使用多路复用(针对HTTP2)
我们先对比三次调用的差异
connectionPool.callAcquirePooledConnection(address, call, null, false)
connectionPool.callAcquirePooledConnection(address, call, routes, false)
connectionPool.callAcquirePooledConnection(address, call, routes, true)
得出
只拿不进行[多路复用]的连接
看似好像也不拿[多路复用]的连接,其实不是。这是针对http2的获取连接。与[多路复用无关]。原因:在isEligible 中如果不是HTTP2将会直接返回false
只拿[多路复用]的连接
那么我们再看看这三次调用的时机。
第一次,就是我们正常流程的获取一个连接。那假如没有拿到,就会 localRouteselector 中选择一个组的路由出来,进行第二次的获取。如果这个时候,还是没有拿到,那么我们就需要自己创建一个连接了。
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())
创建后,为什么?还是在连接池中拿一次呢?原因是:有两个请求同时去访问同一个ip地址,其中 A 已经创建了一个连接,B 也创建了一个连接。这样就有两个连接了,造成资源的浪费。所以当 A 创建后,B再创建后,先去连接池里面再拿一次,如果拿到了,就把自己创建的放弃掉(但又不完全放弃,nextRouteToTry 会把刚刚创建的 route 暂存起来,因为有可能真正去用的时候,A创建的那个连接又被释放了)
那么回归到这段分析的最开始,就是 exchangeFinder的find() 到底做了什么,那这里我小结一下。就是在连接池中找到一个可用的连接,并且生成对应的编码解码器。
然后在回归到 initExchange 的方法中,最后通过 exchangeFinder 和 codec 实例化了一个Exchange 并返回。
第五个拦截器
client.networkInterceptors //这个也是自定义的拦截器,暂时跳过
第六个拦截器 CallServerInterceptor
/** This is the last interceptor in the chain.
* It makes a network call to the server. */
在这个类中搜索 proceed,可以看到并没有这样的结果。
那么这个拦截器,做的事情,就是把请求发到服务器,并且把服务器返回的数据,返回都上一层。
小结
当所有的拦截器都放到了 chain 中,chain 调用 proceed ,流程就会像我们上面分析的那样执行下来。每个拦截器都会做好自己的工作【前置工作】,到最后一个拦截器后,就是有点万事俱备,只欠东风的感觉了。
3.结语
这次分析,全程没有打开别的资料,所以可能会存在一些遗漏或者错误。欢迎大家勘正。同时,如果这篇文章对你起到了作用,欢迎一键三连。点赞,评论,收藏。(老B站选手了)。欢迎大家私信交流~~~
本篇文章后续,会进行多次修正或者补充。以及会及时(没有意外的话,下周末)会发布《OkHttp源码解读逐字稿(2)-OkHttpClient》欢迎大家监督,催更。
以上是关于AndroidOkHttp源码解读逐字稿-拦截器的主要内容,如果未能解决你的问题,请参考以下文章
AndroidOkHttp源码解读逐字稿-OkHttpClient
AndroidOkHttp源码解读逐字稿-OkHttpClient