【Android】OkHttp源码解读逐字稿(1)-拦截器

目录

0.前言

1.OkHttp的简单使用

2.浅析开始

拦截器

链式调用流程示意图

第 0 个拦截器

第一个 拦截器 RetryAndFollowUpInterceptor

第二个拦截器 BridgeInterceptor

第三个拦截器 CacheInterceptor

第四个拦截器 ConnectInterceptor

第五个拦截器

第六个拦截器  CallServerInterceptor

小结

3.结语


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》欢迎大家监督,催更。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q-CODER

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值