从网络请求过程看OkHttp拦截器( 四 )

在这个缓存策略生存的过程中,只有一种情况下会返回缓存,也就是缓存控制不是 no-cache ,并且缓存没过期情况下,就返回缓存,然后设置networkRequest为空 。
所以也就对应上一开始缓存拦截器中的获取缓存后的判断:
// 如果不允许使用网络,但是有缓存,则直接返回缓存if (networkRequest == null) {return cacheResponse!!.newBuilder().cacheResponse(stripBody(cacheResponse)).build().also {listener.cacheHit(call, it)}}连接拦截器(ConnectInterceptor)继续,连接拦截器,之前说了是关于 TCP连接 的 。
object ConnectInterceptor : Interceptor {@Throws(IOException::class)override fun intercept(chain: Interceptor.Chain): Response {val realChain = chain as RealInterceptorChainval exchange = realChain.call.initExchange(chain)val connectedChain = realChain.copy(exchange = exchange)return connectedChain.proceed(realChain.request)}}代码看着倒是挺少的,但其实这里面很复杂很复杂,不着急,我们慢慢说 。
这段代码就执行了一个方法就是 initExchange 方法:
internal fun initExchange(chain: RealInterceptorChain): Exchange {val codec = exchangeFinder.find(client, chain)val result = Exchange(this, eventListener, exchangeFinder, codec)return result}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)}}好像有一点眉目了,找到一个ExchangeCodec类,并封装成一个Exchange类 。
ExchangeCodecExchange明白了,这个连接拦截器(ConnectInterceptor)就是找到一个可用连接呗,也就是TCP连接,这个连接就是用于HTTP请求和响应的 。
你可以把它可以理解为一个 管道 ,有了这个管道,才能把数据丢进去,也才可以从管道里面取数据 。
而这个 ExchangeCodec ,编码解码器就是用来读取和输送到这个管道的一个工具,相当于把你的数据封装成这个连接(管道)需要的格式 。
我咋知道的?我贴一段ExchangeCodec代码你就明白了:
//Http1ExchangeCodec.JAVAfun writeRequest(headers: Headers, requestLine: String) {check(state == STATE_IDLE) { "state: $state" }sink.writeUtf8(requestLine).writeUtf8("rn")for (i in 0 until headers.size) {sink.writeUtf8(headers.name(i)).writeUtf8(": ").writeUtf8(headers.value(i)).writeUtf8("rn")}sink.writeUtf8("rn")state = STATE_OPEN_REQUEST_BODY}这里贴的是 Http1ExchangeCodec 的write代码,也就是Http1的编码解码器 。
很明显,就是将Header信息一行一行写到sink中,然后再由sink交给输出流,具体就不分析了 。只要知道这个编码解码器就是用来处理连接中进行输送的数据即可 。
然后就是这个拦截器的关键了,连接到底是怎么获取的呢?继续看看:
private fun findConnection(): RealConnection {// 1、复用当前连接val callConnection = call.connectionif (callConnection != null) {//检查这个连接是否可用和可复用if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {toClose = call.releaseConnectionNoEvents()}return callConnection}//2、从连接池中获取可用连接if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {val result = call.connection!!eventListener.connectionAcquired(call, result)return result}//3、从连接池中获取可用连接(通过一组路由routes)if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {val result = call.connection!!return result}route = localRouteSelection.next()// 4、创建新连接val newConnection = RealConnection(connectionPool, route)newConnection.connect// 5、再获取一次连接,防止在新建连接过程中有其他竞争连接被创建了if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {return result}//6、还是要使用创建的新连接,放入连接池,并返回connectionPool.put(newConnection)return newConnection}获取连接的过程很复杂,为了方便看懂,我简化了代码,分成了6步 。

  • 1、检查当前连接是否可用 。
怎么判断可用的?主要做了两个判断
1)判断是否不再接受新的连接
2)判断和当前请求有相同的主机名和端口号 。
这倒是很好理解,要这个连接是连接的同一个地方才能复用是吧,同一个地方怎么判断?就是判断 主机名和端口号  。


推荐阅读