重温OKHTTP源码

本文基于OkHttp4.12.0源码分析 官方地址

概括

本篇主要是对okhttp开源库的一个详细解析,包含详细的请求流程分析、各大拦截器的解读等。

使用方法

同步请求:创建一个OKHttpClient对象,一个Request对象,然后利用它们创建一个Call对象,最后调用execute()方法来拿到Response

val client = OkHttpClient()
val request = Request.Builder()
                .url("http://www.baidu.com")
                .build()

val response = client.newCall(request).execute();

异步调用:异步调用不同的在于使用enqueue方法并传入一个Callback接口来获取返回的Response。

val client = OkHttpClient()
val request = Request.Builder()
                .url("http://www.baidu.com")
                .build()
client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                e.printStackTrace()
            }

            override fun onResponse(call: Call, response: Response) {
                Log.d("TAG", "response = " + response.message)
                Log.d("TAG", "response = " + response.body!!.toString())
            }
})

下面关于一些关键类的解析。

OKHttpClient

这是一个请求配置类,采用了建造者模式,方便用户配置一些请求参数,如配置callTimeout,cookie,interceptor等等。

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
@get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher

  @get:JvmName("connectionPool") val connectionPool: ConnectionPool = builder.connectionPool

  /**
   * Returns an immutable list of interceptors that observe the full span of each call: from before
   * the connection is established (if any) until after the response source is selected (either the
   * origin server, cache, or both).
   */
  @get:JvmName("interceptors") val interceptors: List<Interceptor> =
      builder.interceptors.toImmutableList()

....

  constructor() : this(Builder())

....

  /** Prepares the [request] to be executed at some point in the future. */
  override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)


class Builder constructor() {
    internal var dispatcher: Dispatcher = Dispatcher()
    internal var connectionPool: ConnectionPool = ConnectionPool()
    internal val interceptors: MutableList<Interceptor> = mutableListOf()
    internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
    internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
    internal var retryOnConnectionFailure = true
    internal var authenticator: Authenticator = Authenticator.NONE
    internal var followRedirects = true
    internal var followSslRedirects = true
    internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
    internal var cache: Cache? = null
    internal var dns: Dns = Dns.SYSTEM
    internal var proxy: Proxy? = null
    internal var proxySelector: ProxySelector? = null
    internal var proxyAuthenticator: Authenticator = Authenticator.NONE
    internal var socketFactory: SocketFactory = SocketFactory.getDefault()
    internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
    internal var x509TrustManagerOrNull: X509TrustManager? = null
    internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
    internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
    internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
    internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
    internal var certificateChainCleaner: CertificateChainCleaner? = null
    internal var callTimeout = 0
    internal var connectTimeout = 10_000
    internal var readTimeout = 10_000
    internal var writeTimeout = 10_000
    internal var pingInterval = 0
    internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
    internal var routeDatabase: RouteDatabase? = null

....

Request

这里作为请求参数的配置类,本身也采用了建造者模式,但相比OKHttpClient,Request就比较简单了,只有四个参数,分别是url、method、headers、body。

class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,
  @get:JvmName("method") val method: String,
  @get:JvmName("headers") val headers: Headers,
  @get:JvmName("body") val body: RequestBody?,
  internal val tags: Map<Class<*>, Any>
) {

....
open class Builder {
    internal var url: HttpUrl? = null
    internal var method: String
    internal var headers: Headers.Builder
    internal var body: RequestBody? = null
...

Call

请求调用接口,表示这个请求已经准备好可以执行,也可以取消,只能执行一次。

interface Call : Cloneable {
  /** Returns the original request that initiated this call. */
  fun request(): Request

  /**
   * Invokes the request immediately, and blocks until the response can be processed or is in error.
   *
   * To avoid leaking resources callers should close the [Response] which in turn will close the
   * underlying [ResponseBody].
   *
   * ```
   * // ensure the response (and underlying response body) is closed
   * try (Response response = client.newCall(request).execute()) {
   *   ...
   * }
   * ```
   *
   * The caller may read the response body with the response's [Response.body] method. To avoid
   * leaking resources callers must [close the response body][ResponseBody] or the response.
   *
   * Note that transport-layer success (receiving a HTTP response code, headers and body) does not
   * necessarily indicate application-layer success: `response` may still indicate an unhappy HTTP
   * response code like 404 or 500.
   *
   * @throws IOException if the request could not be executed due to cancellation, a connectivity
   *     problem or timeout. Because networks can fail during an exchange, it is possible that the
   *     remote server accepted the request before the failure.
   * @throws IllegalStateException when the call has already been executed.
   */
  @Throws(IOException::class)
  fun execute(): Response

  /**
   * Schedules the request to be executed at some point in the future.
   *
   * The [dispatcher][OkHttpClient.dispatcher] defines when the request will run: usually
   * immediately unless there are several other requests currently being executed.
   *
   * This client will later call back `responseCallback` with either an HTTP response or a failure
   * exception.
   *
   * @throws IllegalStateException when the call has already been executed.
   */
  fun enqueue(responseCallback: Callback)

  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
  fun cancel()

  /**
   * Returns true if this call has been either [executed][execute] or [enqueued][enqueue]. It is an
   * error to execute a call more than once.
   */
  fun isExecuted(): Boolean

  fun isCanceled(): Boolean

  /**
   * Returns a timeout that spans the entire call: resolving DNS, connecting, writing the request
   * body, server processing, and reading the response body. If the call requires redirects or
   * retries all must complete within one timeout period.
   *
   * Configure the client's default timeout with [OkHttpClient.Builder.callTimeout].
   */
  fun timeout(): Timeout

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  public override fun clone(): Call

  fun interface Factory {
    fun newCall(request: Request): Call
  }
}

RealCall

RealCall是Call接口的具体实现类,是应用端与网络层的连接器,输入应用端原始的请求与连接数据,以及网络层返回的response及其他数据流。通过使用方法可知,创建完RealCall对象后,就要调用同步或异步请求方法,所以它里面还包含同步请求execute()与异步请求enqueue()方法。

class RealCall(
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {

....

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)
    }
  }

  override fun enqueue(responseCallback: Callback) {
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }
....

AsyncCall

异步请求调用,是RealCall的一个内部类,本身实现了Runnable接口,被调度器中的线程池所执行。

internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {

....

Dispatcher

调度器,用来调度Call对象,同时包含线程池和异步请求队列,用来存放与执行AsyncCall对象。

流程分析

同步请求

val response = client.newCall(request).execute();

newCall方法就是创建一个RealCall对象,然后执行其execute方法

override fun execute(): Response {
    //CAS判断是否已经被执行了,确保只能执行一次,如果已经被执行了,则抛出异常
    check(executed.compareAndSet(false, true)) { "Already Executed" }
    
    //开启超时监听
    timeout.enter()
    //开启请求监听
    callStart()
    try {
      //调用调度器中的executed方法,调度器只是将call加入到runningSyncCalls队列中
      client.dispatcher.executed(this)
      //调用getResponseWithInterceptorChain方法拿到response
      return getResponseWithInterceptorChain()
    } finally {
      //执行完毕,调度器将该call从runningSyncCalls队列中移除
      client.dispatcher.finished(this)
    }
  }

调度器调用executed犯法,将当前的RealCall对象加入到runningSyncCalls队列中,然后调用getResponseWithInterceptorChain方法拿到response。

异步请求

client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        e.printStackTrace()
    }

    override fun onResponse(call: Call, response: Response) {
        
    }
})


RealCall.kt
override fun enqueue(responseCallback: Callback) {
    check(executed.compareAndSet(false, true)) { "Already Executed" }
    //开启请求监听
    callStart()
    //新建一个AsyncCall对象,通过调度器enqueue方法加入到readyAsyncCalls队列中
    client.dispatcher.enqueue(AsyncCall(responseCallback))
}

Dispatcher.kt
internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      readyAsyncCalls.add(call)

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if (!call.call.forWebSocket) {
        //通过域名来查找有没有相同域名的请求,有则复用
        val existingCall = findExistingCallWithHost(call.host)
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    //执行请求
    promoteAndExecute()
  }

private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()

    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      //遍历readyAsyncCalls队列
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()
        //检查runningAsyncCalls的数量
        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
        //同域名最大请求数
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.

        i.remove()
        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
  }


RealCall.kt
fun executeOn(executorService: ExecutorService) {
      client.dispatcher.assertThreadDoesntHoldLock()

      var success = false
      try {
        //执行AsyncCall这个runnable
        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) // This call is no longer running!
        }
      }
    }


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)
        }
      }
    }
  }

获取Response

@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 {
      //根据拦截器责任链来获取response
      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)
      }
    }
  }

RealInterceptorChain.kt

@Throws(IOException::class)
  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
  }

Interceptor

该接口只声明了一个拦截器方法,同时,在其内部,还有一个Chain接口,核心方法是proceed(request)处理请求来获取response

各类拦截器

client.interceptors:由开发者自定义的拦截器,会在所有拦截器处理之前进行最早的拦截处理,可用于添加一些公共参数,如自定义header、自定义log等等。

class HeadInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request().newBuilder()
            .addHeader("device-serial", "********8")
            .build()
        return chain.proceed(request)
    }
}

fun test() {
    val client = OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.MILLISECONDS)
        .readTimeout(15, TimeUnit.MILLISECONDS)
        .writeTimeout(15, TimeUnit.MILLISECONDS)
        .addInterceptor(HeadInterceptor())
        .build()
}

RetryAndFollowUpInterceptor:这里会对连接做一些初始化的工作,以及请求失败的重试工作,重定向的后续请求工作。

@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 newExchangeFinder = true
    var recoveredFailures = listOf<IOException>()
    while (true) {
      //这里创建一个exchangeFinder,ConnectInterceptor会用到
      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.
        //尝试使用上一个已经构建好的response,body为空
        if (priorResponse != null) {
          response = response.newBuilder()
              .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
              .build()
        }

        val exchange = call.interceptorScopedExchange
        //根据responseCode来判断,是否构建一个新的response并返回来重试或者重定向
        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)
      }
    }
  }

  /**
   * Report and attempt to recover from a failure to communicate with a server. Returns true if
   * `e` is recoverable, or false if the failure is permanent. Requests with a body can only
   * be recovered if the body is buffered or if the failure occurred before the request has been
   * sent.
   */
  //判断是否需要重连
  private fun recover(
    e: IOException,
    call: RealCall,
    userRequest: Request,
    requestSendStarted: Boolean
  ): Boolean {
    // The application layer has forbidden retries.
    //客户端禁止重试
    if (!client.retryOnConnectionFailure) return false
    
    //不能再次发送该请求体
    // We can't send the request body again.
    if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
    
    //发生的异常时致命的,无法恢复
    // This exception is fatal.
    if (!isRecoverable(e, requestSendStarted)) return false

    //没有跟多的路由来尝试重连
    // No more routes to attempt.
    if (!call.retryAfterFailure()) return false

    // For failure recovery, use the same route selector with a new connection.
    return true
  }

BridgeInterceptor:这是客户端与服务器之间的沟通桥梁,负责将用户构建的请求转换为服务器需要的请求,以及将网络请求返回回来的响应转换为用户可用的相应

@Throws(IOException::class)
  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()
  }

CacheInterceptor:这里是缓存相关的处理,会根据用户在OkhttpClient里定义的缓存配置,然后结合请求新建一个缓存策略,由他来判断使用网络还是缓存来构建response

@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) {
      // The cache candidate wasn't applicable. Close it.
      cacheCandidate.body?.closeQuietly()
    }
    //如果网络被禁止,同时缓存也是空的,构建一个code为504的response返回
    // 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) {
      //如果由缓存,且返回的code为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()

        // 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)
          }
        }
      }
      //根据请求方法来判定缓存是否有效,只对Get请求进行缓存
      if (HttpMethod.invalidatesCache(networkRequest.method)) {
        try {
          cache.remove(networkRequest)
        } catch (_: IOException) {
          // The cache cannot be written.
        }
      }
    }

    return response
  }

ConnectInterceptor:这里主要是负责建立连接,会建立tcp或者tls连接

@Throws(IOException::class)
  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)
}

client.networkInterceptors:这也是开发者自己设置的,但所处位置不同,用处也不相同

CallServerInterceptor:这里是进行网络请求和响应的地方。将请求头与请求体发送给服务器,以及解析服务器返回的response。

以下是整体调用流程

知识点:

建造者模式:不论是OkHttpClient、Request还是Response,都是用了建造者模式,因为这几个类中都有很多参数,需要供用户选择需要的参数来构建其想要的实例。

工厂方法模式:帮助生成复杂对象,比如OkHttpClient.newCall(request Request)来创建call对象

责任链模式:这里将拦截器构建成拦截器责任链,然后按顺序从上往下执行,得到response后,从下往上传。

线程安全

使用原子类,重入锁等实现线程安全

数据结构

采用队列实现,更符合整体网络请求的要求,比如先到先得。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/518902.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

免费微信小程序源码分享~搭起来改一下就可以【创业】

【前言】现在很多人都想做微信小程序创业搞钱&#xff0c;但是苦于不会开发或过高的开发成本只能放弃&#xff0c;下面我收集了几套微信小程序的源码供各位有梦想的同学免费使用~ 这些小程序代码都包含了客户端和管理端&#xff0c;你搭建起来就可以开始创业搞钱了~ 下载链接&a…

PostgreSQL 文章下架 与 热更新和填充可以提升数据库性能

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;&#xff08;…

4月4日今日预告:printf+scanf+分支循环,if语句,else悬空问题,加油,干干干这篇文章三个小时半了,从愚人节被告知今天就有课程-今日4/3,

今天中午知道要爆肝的C语言的&#xff0c;今天本来作业好多的&#xff1b; 干了&#xff0c;家人们 做一些补充&#xff1a; 一&#xff1a;printf() 参数与占位符对应关系 printf() 参数与占位符是⼀⼀对应关系&#xff0c;如果有 n 个占位符&#xff0c; printf() 的参数…

使用docker-tc对host容器进行限流

docker-tc是一个github开源项目&#xff0c;项目地址是https://github.com/lukaszlach/docker-tc。 运行docker-tc docker run -d \ --name docker-tc \ --network host \ --cap-add NET_ADMIN \ --restart always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /var…

通过vite创建项目

一、VUE3官网 Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org) 二、通过Vite创建项目 1、在cmd窗口下&#xff0c;全局安装vite //使用国内镜像源 npm config set registryhttps://registry.npmmirror.com//安装最新版vite npm install -g vitelatest Vite | 下一代…

阿里云、腾讯云、华为云优惠券领取攻略

随着云计算技术的日益成熟和普及&#xff0c;越来越多的企业和个人开始选择使用云服务商来满足自己的数据存储、计算和处理需求。阿里云、腾讯云、华为云作为国内领先的云服务商&#xff0c;提供了丰富多样的云产品和服务。而为了吸引更多用户&#xff0c;它们也时常会推出各种…

4.4学习总结

一.线段树概念 一.定义: 线段树是一种二叉搜索树&#xff0c;而二叉搜索树&#xff0c;首先满足二叉树&#xff0c;即每个结点最多有两颗子树&#xff0c;并且是一颗搜索树&#xff0c;我们要知道&#xff0c;线段树的每个结点都存储了一个区间&#xff0c;也可以理解成一个线…

文件系统监视库(watchdog)

Python Watchdog库是一个用于监视文件系统变化的Python第三方库。以下是关于Watchdog库的详细介绍&#xff1a; 功能&#xff1a;Watchdog库能够监控文件和目录的创建、修改、删除和移动等操作。它通过使用底层原生API&#xff08;如Windows的ReadDirectoryChangesW、Linux 2.6…

Golang学习笔记

Golang学习笔记 安装Golang 来源&#xff1a;linux 安装 golang - 知乎 (zhihu.com) 由于我用的是linux系统&#xff0c;所以本文采用linux的安装方式介绍&#xff0c;如果你使用的是Windows/Mac 也可以看下该文章&#xff0c;或者自己去下列地址进行操作。 Download and in…

react中配置webpack:使用@代表src目录

在vue的项目中可以使用表示src目录&#xff0c;使用该符号表示绝对路径&#xff0c;那么在react中想要使用怎么办呢&#xff1f; 在react中使用表示src目录是需要在webpack中配置的&#xff0c;在核心模块node_modules-》react-scripts-》config-》webpack.config.js中搜索找到…

基于SSM的品牌银饰售卖平台(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的品牌银饰售卖平台&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring …

Windows11下Docker使用记录(一)

Docker使用记录&#xff08;一&#xff09; 简单介绍Docker安装Docker 常用命令Docker 可视化Docker 使用GPU可视化rviz、gazebo 在进行ROS项目开发时&#xff0c;如果只有一台Windows电脑&#xff0c;我们可以考虑使用WSL或Docker来搭建ROS环境。在尝试了两种方式后&#xff0…

代码随想录第31天 | 455.分发饼干 、376. 摆动序列、53. 最大子序和

一、前言 参考文献&#xff1a;代码随想录 今天的内容是贪心算法&#xff0c;这个算法分为两个极端&#xff0c;一个极端是很简单&#xff0c;靠常识就可以解出来&#xff0c;另外一个是&#xff0c;你怎么想也想不出来&#xff0c;只能看题解的那种。 and 对第一天和第二天…

Spring源码解析上

spring源码解析 整体架构 defaultListableBeanFactory xmlBeanDefinitionReader 创建XmlBeanFactory 对资源文件进行加载–Resource 利用LoadBeandefinitions(resource)方法加载配置中的bean loadBeandefinitions加载步骤 doLoadBeanDefinition xml配置模式 validationMode 获…

树的基本概念与二叉树

文章目录 树的基本概念与二叉树一、树的概念和结构1. 树的概念2. 树的相关概念 二、树的存储1. 左孩子右兄弟表示法2. 双亲表示法 三、二叉树1. 特殊的二叉树1.1 满二叉树1.2 完全二叉树 树的基本概念与二叉树 一、树的概念和结构 1. 树的概念 树是一种非线性的数据结构,它是…

Liunx进程信号

进程信号 进程信号什么是信号liunx信号种类 前后台进程前后台进程的概念 进程信号的产生键盘产生 阻塞信号信号的捕捉用户态和内核态 信号的捕捉函数 进程信号 什么是信号 信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知…

计算机专业,不擅长打代码,考研该怎么选择?

考研其实和你的代码能力关系不大 所以在选学校以前可以看看有哪些学校复试是要求上机撸代码的&#xff0c;可能会要求比较严 初试真的不用担心代码问题&#xff0c;我也是基本零编程能力就开始备考考研的... 本人双非科班出身备考408成功上岸&#xff0c;在这里也想给想考40…

先登杯·14天创作挑战营·第④期~ 等你来战!

文章目录 ⭐️ 活动介绍⭐️ 活动详情⭐️ 活动奖品⭐️ 活动流程​⭐️ 评审规则⭐️ 报名&投稿注意事项⭐️ 活动组织 ​ 活动报名入口&#xff1a;https://bbs.csdn.net/topics/618374514 本次活动与官方活动及其他博主的创作型活动并不冲突&#xff01; ​ ​ ⭐️…

练习 22 Web [极客大挑战 2019]BuyFlag

php弱类型比较&#xff0c;注意Cookie值&#xff0c;php利用数组赋值进行绕过,科学计数法 很明显是弱类型比较&#xff0c;之前的练习题已经遇到过 构造password404adsffd&#xff0c;后面随便什么字母都行 然后 money100000000 然后在student这里卡了很久&#xff0c;post…

python解决既约分数(gcd)

题目&#xff1a; 如果一个分数的分子和分母的最大公约数是1&#xff0c;这个分数成为既约分数。例如3/4、1/8、7/1、都是既约分数。请问&#xff0c;有多少个既约分数&#xff0c;分子和分母都是1到2020之间的整数&#xff08;包括1和2020&#xff09;&#xff1f; 分析&#…