OkHttp 源碼簡單解讀

OkHttp已經(jīng)很出名仅偎,這里就不啰嗦了奶赠,直接進(jìn)入主題,以下代碼演示均使用Kotlin熙尉。
OkHttp版本:4.9.0

我們先從發(fā)起一個(gè)簡單的請求來說起联逻。

  val url = "https://api.github.com/users/huangcv/repos"
  val client = OkHttpClient()
  val request = Request
          .Builder()
          .url(url)
          .build()
  client.newCall(request)
          .enqueue(object : Callback {
              override fun onFailure(call: Call, e: IOException) {

              }

              override fun onResponse(call: Call, response: Response) {
                   println("Response status code:${response.code}")
              }
          })

結(jié)果如下:

結(jié)果

一個(gè)簡單的請求就結(jié)束了。
接下來我們簡單的來分析下源碼检痰,先從最近的地方開始著手看源碼包归,就是.enqueue()這里,我們點(diǎn)擊去發(fā)現(xiàn)是一個(gè)接口铅歼,然后退出來找上一個(gè)節(jié)點(diǎn)的代碼公壤,就是.newCall()這里,看創(chuàng)建了一個(gè)怎樣的Call對象椎椰。

  /** Prepares the [request] to be executed at some point in the future. */
  override fun newCall(request: Request): Call = RealCall(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 {
  ......
}

構(gòu)造函數(shù)中第一個(gè)參數(shù),就是我們最開始創(chuàng)建的那個(gè)OkHttpClient俭识,這個(gè)類就相當(dāng)于一個(gè)大總管慨削,一些通用的配置都在這里,比如讀寫超時(shí)時(shí)間套媚,設(shè)置代理缚态,緩存等都是在這個(gè)類中設(shè)置的。
第二個(gè)參數(shù):Request堤瘤,初始的請求玫芦,就是我們newCall傳進(jìn)來的Request,而且看它的名字是originalRequest本辐,因?yàn)閺奈覀儼l(fā)起請求到最終將一個(gè)請求發(fā)送出去這個(gè)過程中Request會(huì)被包裝好幾層桥帆。
第三個(gè)從參數(shù):是否是WebSocket,默認(rèn)false慎皱,不是WebSocket請求老虫。

所以到這一步我們就清楚了,newCall的作用就是創(chuàng)建一個(gè)RealCall對象茫多,將OkHttpClient和Request傳遞進(jìn)去祈匙。

接下來我們來看下RealCall具體干了什么?
還記得我們第一次從 enqueue函數(shù)點(diǎn)進(jìn)去的嗎,因?yàn)槔锩媸且粋€(gè)接口夺欲,所以我們才來到了newCall跪帝,因?yàn)镽ealCall實(shí)現(xiàn)了Call這個(gè)接口,接下來我們就要看下RealCall實(shí)現(xiàn)了Call之后的enqueue函數(shù)了些阅。
點(diǎn)進(jìn)去可以看到:

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

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

第一行檢查這個(gè)call是否被執(zhí)行過了
第二行:callStart()伞剑,我們點(diǎn)進(jìn)去看下;

  private fun callStart() {
    this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
    eventListener.callStart(this)
  }

第一行用于跟蹤錯(cuò)誤市埋,用于錯(cuò)誤分析黎泣。
第二行:eventListener.callStart(this),事件監(jiān)聽器的回調(diào)函數(shù)腰素。

這里沒有重要的信息聘裁,我們再回過去看第三行代碼:
client.dispatcher.enqueue(AsyncCall(responseCallback))
這句話是重點(diǎn),我們需要看client的dispatcher的enqueue函數(shù)干了什么弓千,并且這里還傳入了一個(gè)AsyncCall對象。

我們先看這個(gè)dispatcher是個(gè)什么東西献起?
點(diǎn)進(jìn)去:

  @get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher

我們在點(diǎn)進(jìn)去洋访,到dispatcher類中看下:

/**
* Policy on when async requests are executed.
*
* Each dispatcher uses an [ExecutorService] to run calls internally. If you supply your own
* executor, it should be able to run [the configured maximum][maxRequests] number of calls
* concurrently.
*/
class Dispatcher constructor() {
   ......
}

我們看到類描述,dispatcher是用來做線程調(diào)度用的谴餐,因?yàn)橥粫r(shí)間我們可能發(fā)起了好幾個(gè)請求姻政,因?yàn)槊恳粋€(gè)請求都是一個(gè)線程,也就是說線程的執(zhí)行岂嗓,就代表著請求的開始汁展,所以實(shí)質(zhì)上dispatcher是對線程的調(diào)度管理,從他執(zhí)行過程中用的是ExecutorService就可以進(jìn)一步說明厌殉,dispatcher是一個(gè)多線程調(diào)度管理器食绿。
也就是說我們在分析dispatcher里面的代碼,基本上都是關(guān)于調(diào)度的公罕。

對于Dispatcher我們可以簡單來看下:

  @get:Synchronized var maxRequests = 64

可以同時(shí)進(jìn)行請求的最大數(shù)量器紧,超過這個(gè)數(shù)量,就要進(jìn)行等待楼眷。

  @get:Synchronized var maxRequestsPerHost = 5

每個(gè)主機(jī)下同時(shí)進(jìn)行請求的最大數(shù)量铲汪,如果通過主機(jī)超過這個(gè)數(shù)量,就需要等待罐柳。

  @get:Synchronized
  @get:JvmName("executorService") val executorService: ExecutorService

Diapatcher 底層對多線程的管理工具掌腰,使用的是Java自帶的多線程管理工具。

好了我們現(xiàn)在知道Dispatcher是干什么用的了张吉,之后我們再看下dispatcher.enqueue()干了什么齿梁?
點(diǎn)進(jìn)去看下:

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

可以看到最關(guān)鍵的就是兩句代碼,中間那一坨注釋已經(jīng)說的很明白了:就是針對同一個(gè)主機(jī)芦拿,檢查當(dāng)前正在執(zhí)行的請求數(shù)量士飒,是否超過最大請求數(shù)查邢,這個(gè)當(dāng)前正在請求的請求數(shù)對于每個(gè)請求都是共享的。

  1. readyAsyncCalls.add(AsyncCall)
  2. promoteAndExecute()
    第一句是將我們傳進(jìn)來的AsyncCall對象添加到一個(gè)雙向隊(duì)列中酵幕,這里隊(duì)列中存放的都是準(zhǔn)備要執(zhí)行但是還沒有執(zhí)行的任務(wù)扰藕。
    再看第二句:promoteAndExecute(),從字面意思來看執(zhí)行異步任務(wù)芳撒。
    來看下具體的代碼:
  /**
   * Promotes eligible calls from [readyAsyncCalls] to [runningAsyncCalls] and runs them on the
   * executor service. Must not be called with synchronization because executing calls can call
   * into user code.
   *
   * @return true if the dispatcher is currently running calls.
   */
  private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()

    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()

        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
  }

代碼稍微多了一點(diǎn)邓深,其主要功能就是:
將上一步已經(jīng)準(zhǔn)備好但還沒有執(zhí)行的隊(duì)列中的請求都拿出來遍歷一遍,篩選出可以執(zhí)行的請求笔刹,然后用executorService來執(zhí)行這些所有可執(zhí)行的請求芥备。
我們具體來看下:
首先從AsyncCallDeque隊(duì)列中篩選出可執(zhí)行的請求,條件是沒有超過最大請求數(shù)(maxRequests)舌菜,并且所請求的主機(jī)沒有超過最大請求數(shù)(maxRequestsPerHost)萌壳,滿足這兩個(gè)條件,該請求即滿足可執(zhí)行請求的條件日月,然后加入到可執(zhí)行請求的集合中(executableCalls)袱瓮,并且添加到正在執(zhí)行請求的隊(duì)列中(runningAsyncCalls),用于后面做記錄用的爱咬。接下來就是拿著可執(zhí)行的請求的集合(executableCalls)遍歷執(zhí)行這些請求并執(zhí)行尺借,怎么執(zhí)行呢,把這些請求都丟給ExecutorService來進(jìn)行執(zhí)行精拟,也就是調(diào)用AsyncCall的executeOn函數(shù)燎斩。
點(diǎn)進(jìn)去看下:

    /**
     * Attempt to enqueue this async call on [executorService]. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    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) // This call is no longer running!
        }
      }
    }

代碼并不多,我們來簡單分析下executeOn函數(shù)蜂绎。
關(guān)鍵代碼:executorService.execute(this)栅表,也就是說到這里線程發(fā)生了切換,不管之前的線程是什么荡碾,到這里統(tǒng)一都是后臺線程谨读。
至于后面的都是做一些掃尾工作,比如請求執(zhí)行的過程中是否報(bào)錯(cuò)坛吁,報(bào)錯(cuò)了進(jìn)行狀態(tài)的回調(diào)以及將該請求標(biāo)記為請求完成劳殖。這就不細(xì)聊了。

我們接下來看:executorService.execute(this)這句代碼拨脉,既然傳入的是this哆姻,那么它一定實(shí)現(xiàn)了Runnable接口,所以我們就找AsyncCall中的run函數(shù)玫膀。如下:

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

關(guān)鍵代碼:val response = getResponseWithInterceptorChain()矛缨,也就是說在這里我們可以直接拿到請求的響應(yīng)。接下來就是對響應(yīng)的一些處理,比如說:responseCallback.onResponse(this@RealCall, response)箕昭,回調(diào)請求響應(yīng)灵妨,即成功。
responseCallback.onFailure(this@RealCall, e)落竹,出現(xiàn)異常了回調(diào)失敗泌霍。
這里的responseCallback就是我們最開始從client.newCall(request).enqueue(Callback)傳遞進(jìn)來的Callback。
接下來最終執(zhí)行
client.dispatcher.finished(this)函數(shù)述召,將該請求標(biāo)記為完成朱转。

經(jīng)過上述步驟之后也就意味著整個(gè)請求結(jié)束了。
結(jié)束了嗎积暖?顯然沒有藤为,因?yàn)槲覀冞€沒有看是如果拿到Response的,具體干活的地方還沒看呢夺刑。

接下來我們就要進(jìn)入到另一個(gè)全新的階段缅疟,根據(jù)請求獲取響應(yīng),這也是每一個(gè)請求最最最重要的地方性誉,同樣這樣是OkHttp最最最重要和硬核的部分窿吩。

在說getResponseWithInterceptorChain()函數(shù)之前,我們有必要先了解下OkHttp的各種配置错览,這對后面理解getResponseWithInterceptorChain()有很好的幫助。

OkHttp配置簡單介紹:

  • dispatcher 多線程調(diào)度管理器煌往,前面我們已經(jīng)說過了倾哺,這里就不細(xì)說了。
  • connectionPool 連接池刽脖,可以類比線程池羞海。連接池內(nèi)部存儲(chǔ)了一批的連接,當(dāng)需要用到連接的時(shí)候而不是先創(chuàng)建曲管,而是先看下池中是否有可重用的却邓,如果沒有再行創(chuàng)建,達(dá)到快速使用和創(chuàng)建院水,如果一個(gè)連接不再被使用的時(shí)候可以配置時(shí)間來進(jìn)行回收腊徙。池的作用就是批量管理對象,通過重用和自動(dòng)回收達(dá)到一種性能和資源占用的動(dòng)態(tài)平衡檬某。
  • interceptors 是一個(gè)攔截器的集合撬腾。
  • networkInterceptors 也是一個(gè)攔截器的集合。
  • eventListenerFactory 用來生產(chǎn)EventListener的一個(gè)工廠恢恼,這個(gè)EventListener我們前面已經(jīng)說過了民傻,它是OkHttp各個(gè)過程和節(jié)點(diǎn)的事件監(jiān)聽器,通過設(shè)置EventListener我們可以監(jiān)聽到,請求過程中的各個(gè)節(jié)點(diǎn)的事件漓踢。
  • retryOnConnectionFailure 當(dāng)連接失敗或者請求失敗時(shí)是否重連牵署,默認(rèn)是true,失敗重連喧半。
  • authenticator 自動(dòng)進(jìn)行認(rèn)證修正的工具奴迅。
  • followRedirects 是否跟蹤重定向,默認(rèn)為true薯酝,當(dāng)響應(yīng)碼為重定向301/302時(shí)半沽,如果為true時(shí),將會(huì)重新進(jìn)行重定向之后的請求吴菠,然后將結(jié)果返回者填,如果為false時(shí),將直接回調(diào)做葵,請求結(jié)束占哟。
  • followSslRedirects 當(dāng)上面followRedirects為true的時(shí)候,當(dāng)出現(xiàn)重定向時(shí)發(fā)生協(xié)議切換時(shí)酿矢,是否繼續(xù)新的協(xié)議的請求榨乎,默認(rèn)為true。
  • cookieJar 用來存儲(chǔ)Cookie瘫筐,默認(rèn)都是空實(shí)現(xiàn)蜜暑,如果需要保存Cookie,需要自己實(shí)現(xiàn)策肝。
  • cache 緩存肛捍。
  • dns 域名系統(tǒng)(Domain Name System),用于解析域名之众。最終使用的還
    是Java的InetAddress.getAllByName()拙毫,這里不再細(xì)說了。
  • proxy 代理棺禾。分為:直連缀蹄,HTTP以及Socket。
  • proxySelector 代理選擇器膘婶。從一個(gè)列表中選擇可使用的代理缺前,如果沒有設(shè)置proxy,默認(rèn)為直連竣付。
  • proxyAuthenticator 代理獨(dú)立的驗(yàn)證機(jī)制诡延,當(dāng)代理出現(xiàn)需要授權(quán)時(shí),通過這個(gè)來進(jìn)行給代理授權(quán)古胆。
  • socketFactory HTTP本質(zhì)的連接就是建立Socket肆良,所建立的Socket就是從這里的工廠創(chuàng)建的筛璧。
  • sslSocketFactoryOrNull 同上個(gè)同理,當(dāng)我們需要建立TLS/SSL加密連接時(shí)的Socket對象就是從這個(gè)sslSocketFactory來創(chuàng)建的惹恃。
  • x509TrustManager HTTPS 建立連接時(shí)的證書驗(yàn)證器夭谤,X509是證書的一種格式,所有的HTTPS連接使用的都是X509格式的證書巫糙。
  • connectionSpecs 連接規(guī)范/標(biāo)準(zhǔn)朗儒。就是HTTPS在建立連接時(shí),客戶端需要給服務(wù)器發(fā)送支持的TLS版本
    以及支持的加密套件(對稱加密参淹,非對稱加密醉锄,哈希等)。
  • protocols 支持的HTTP協(xié)議的版本號浙值,HTTP1.0恳不,HTTP1.1,SPYD3.1,HTTP2,H2_PRIOR_KNOWLEDGE。
  • hostnameVerifier 主機(jī)名驗(yàn)證器开呐,屬于證書驗(yàn)證機(jī)制中的烟勋,我們客戶端驗(yàn)證證書主要是驗(yàn)證:1.證書的合法性,使用X509TrustMangaer來驗(yàn)證 2.是否是所訪問的網(wǎng)站的證書筐付,這里的第二步的驗(yàn)證就是使用HostnameVerifier來驗(yàn)證的卵惦。
  • certificatePinner 證書強(qiáng)制驗(yàn)證的內(nèi)容,就是自己寫的代碼瓦戚,增強(qiáng)了驗(yàn)證的邏輯性沮尿。如果certificatePinner的驗(yàn)證不通過,即是證書驗(yàn)證是合法的较解,整個(gè)證書的驗(yàn)證流程也不會(huì)通過蛹找。
  • certificateChainCleaner 這個(gè)也是證書驗(yàn)證機(jī)制中的,它的功能主要是操作X509TrustManager 驗(yàn)證證書的合法性哨坪。
  • callTimeoutMillis 呼叫超時(shí),但是完整的呼叫是沒有超時(shí)的乍楚,但是呼叫中的連接当编,讀寫是有可能超時(shí)的。
  • connectTimeoutMillis 連接超時(shí)徒溪。默認(rèn)10s忿偷。
  • readTimeoutMillis 連接的讀操作超時(shí)時(shí)間,默認(rèn)10s臊泌。
  • writeTimeoutMillis 連接的寫操作超時(shí)時(shí)間鲤桥,默認(rèn)10s。
  • pingIntervalMillis 心跳間隔渠概,這個(gè)是針對WebSocket和HTTP2來說的茶凳,默認(rèn)不發(fā)送心跳嫂拴。

經(jīng)過以上部分的鋪墊,我們接下來就要進(jìn)入最最最硬核的部分了贮喧,有了以上部分的鋪墊筒狠,我們能更好的理解getResponseWithInterceptorChain這個(gè)函數(shù)里面的東西。

不廢話箱沦,開始辩恼。

我們點(diǎn)進(jìn)去看下 getResponseWithInterceptorChain這個(gè)函數(shù)的具體代碼如下:

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

猛一看,我k谓形,代碼有點(diǎn)多啊灶伊,不過沒關(guān)系,老規(guī)矩寒跳,把代碼羅列一些聘萨,分分類,就會(huì)很清楚了冯袍。
第一部分匈挖,合并Interceptor,把默認(rèn)和自定的Interceptor都合并到一起康愤。

    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)

第二部分儡循,構(gòu)建真正的InterceptorChain,對第一步的Interceptors進(jìn)行封裝和管理征冷,為接下來的鏈?zhǔn)焦ぷ髯鰷?zhǔn)備择膝。

  val chain = RealInterceptorChain(
        call = this,
        interceptors = interceptors,
        index = 0,
        exchange = null,
        request = originalRequest,
        connectTimeoutMillis = client.connectTimeoutMillis,
        readTimeoutMillis = client.readTimeoutMillis,
        writeTimeoutMillis = client.writeTimeoutMillis
    )

第三部分,執(zhí)行第一個(gè)Interceptor检激,讓整個(gè)InterceptorChain開始轉(zhuǎn)起來肴捉,當(dāng)執(zhí)行完第一步合并的所有Interceptor時(shí),整個(gè)流程就結(jié)束了叔收,此次的請求也結(jié)束了齿穗,就拿到了響應(yīng),這么神奇饺律?可是沒看到干什么扒砸场?那是因?yàn)榉庋b的太好了复濒。我們接下里仔細(xì)來看下這些代碼脖卖。

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

在看之前,我們下來了解下說明是鏈?zhǔn)焦ぷ髑删保哪P腿缦拢?/p>

Chain

一個(gè)接一個(gè)的從左往右執(zhí)行到最后一個(gè)畦木,然后再從右往左一個(gè)一個(gè)的執(zhí)行回來。就是每一個(gè)Interceptor都有前置工作砸泛,中置工作和后置工作十籍,而且最后一個(gè)節(jié)點(diǎn)有點(diǎn)特殊蛆封,因?yàn)樗鼪]有中置工作,所以導(dǎo)致它的前置和后置工作合在一起了妓雾,就只剩下一個(gè)工作娶吞。這個(gè)模型也很容易加入一些節(jié)點(diǎn),比如想在最前面加入一個(gè)初始化工作的節(jié)點(diǎn)械姻,將準(zhǔn)備好的Interceptor直接放到第一個(gè)位置妒蛇,然后開始執(zhí)行即可。

也就是說我們將第一部分的那一坨Interceptor看完楷拳,整個(gè)請求->響應(yīng)的過程也就都明白了绣夺。
好了接下來我們開始詳細(xì)看下第一部分的各個(gè)Interceptor。
因?yàn)?code>client.interceptors 和 client.networkInterceptors都是用戶自定義的Interceptor欢揖,這里就沒必要看了陶耍,我們只看內(nèi)置的Interceptor,而且我們只需要弄明白每一個(gè)Interceptor的前置和后置工作即可她混。

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

    點(diǎn)進(jìn)RetryAndFollowUpInterceptor看下它的intercept函數(shù)烈钞,我k授药,代碼有點(diǎn)多随静,不要慌莺褒,穩(wěn)住闰非,按前,中僚楞,后置分開來看就好了岸裙。

    • 前置工作建芙。

      call.enterNetworkInterceptorExchange(request, newExchangeFinder)
      

      這里我們先放一放它的前置工作来累,我們先分析中置和后置工作砚作。因?yàn)閺乃拿只蛘叽a中的while(true)可以看出來,它是做連接失敗重連和重定向跟蹤的功能嘹锁,我們仔細(xì)想一下它的工作流程就能明白它的工作具體是干什么的葫录。

    • 中置工作

       realChain.proceed(request)
      

      交棒給下一個(gè)Interceptor。

    • 后置工作

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

      代碼中除了response = realChain.proceed(request);這句代碼之外领猾,剩下的都是它的后置工作压昼。
      它的后置工作分為兩類,第一個(gè)各種異常和ResponseCode->3XX瘤运,都需要continue,再轉(zhuǎn)一次匠题;第二類請求成功拯坟,直接return response,結(jié)束循環(huán)韭山。
      接下來我們具體看下整個(gè)后置工作:
      !recover(e.lastConnectException, call, request, requestSendStarted = false)
      這句代碼的意思就是出現(xiàn)異常的情況下郁季,判斷當(dāng)前狀態(tài)是否可以繼續(xù)重試發(fā)起請求冷溃,我們點(diǎn)進(jìn)去看下具體邏輯:

      private fun recover(
        e: IOException,
        call: RealCall,
        userRequest: Request,
        requestSendStarted: Boolean
      ): Boolean {
        // The application layer has forbidden retries.
        // 用戶是否設(shè)置了連接失敗重連,默認(rèn)為true梦裂,我們在之前的OkHttpClient中已經(jīng)分析了這個(gè)配置了似枕。
        if (!client.retryOnConnectionFailure) return false
      
        // We can't send the request body again.
        //以下情況是不可以重連的,已經(jīng)開始發(fā)送請求并且該請求只能發(fā)送一次
        if (requestSendStarted && requestIsOneShot(e, userRequest)) return false  
      
        // This exception is fatal.
        // 根據(jù)異常類型判斷是否可以恢復(fù)重連
        if (!isRecoverable(e, requestSendStarted)) return false
      
        // No more routes to attempt.
        // 當(dāng)連接失敗時(shí)判斷是否還有其他線路可以重連
        if (!call.retryAfterFailure()) return false
      
        // For failure recovery, use the same route selector with a new connection.
        // 走到這里說明該請求確實(shí)可以進(jìn)行重連年柠。
        return true
      }
      

      注釋其實(shí)已經(jīng)說的很明白凿歼,這段代碼沒什么難度,以上代碼是出錯(cuò)情況下來判斷是否可以重連冗恨。
      下面代碼則是正常響應(yīng)的情況下來判斷是否需要重連答憔,主要是處理重定向。

       // 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
      

      接下里重點(diǎn)看下val followUp = followUpRequest(response, exchange)掀抹;
      這個(gè)方法的主要作用就是根據(jù)響應(yīng)碼來判斷是否需要重連虐拓,我們點(diǎn)進(jìn)去看下:

      @Throws(IOException::class)
      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 -> {
          val selectedProxy = route!!.proxy
          if (selectedProxy.type() != Proxy.Type.HTTP) {
            throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy")
          }
          return client.proxyAuthenticator.authenticate(route, userResponse)
        }
      
        HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)
      
        HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
          return buildRedirectRequest(userResponse, method)
        }
      
        HTTP_CLIENT_TIMEOUT -> {
          // 408's are rare in practice, but some servers like HAProxy use this response code. The
          // spec says that we may repeat the request without modifications. Modern browsers also
          // repeat the request (even non-idempotent ones.)
          if (!client.retryOnConnectionFailure) {
            // The application layer has directed us not to retry the request.
            return null
          }
      
          val requestBody = userResponse.request.body
          if (requestBody != null && requestBody.isOneShot()) {
            return null
          }
          val priorResponse = userResponse.priorResponse
          if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
            // We attempted to retry and got another timeout. Give up.
            return null
          }
      
          if (retryAfter(userResponse, 0) > 0) {
            return null
          }
      
          return userResponse.request
        }
      
        HTTP_UNAVAILABLE -> {
          val priorResponse = userResponse.priorResponse
          if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {
            // We attempted to retry and got another timeout. Give up.
            return null
          }
      
          if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
            // specifically received an instruction to retry without delay
            return userResponse.request
          }
      
          return null
        }
      
        HTTP_MISDIRECTED_REQUEST -> {
          // OkHttp can coalesce HTTP/2 connections even if the domain names are different. See
          // RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then
          // we can retry on a different connection.
          val requestBody = userResponse.request.body
          if (requestBody != null && requestBody.isOneShot()) {
            return null
          }
      
          if (exchange == null || !exchange.isCoalescedConnection) {
            return null
          }
      
          exchange.connection.noCoalescedConnections()
          return userResponse.request
        }
      
        else -> return null
        }
      }
      

      代碼雖然不少,但是卻很好理解傲武。主要是根據(jù)ResponseCode處理重定向的問題蓉驹,就是3XX系列。

      HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
          return buildRedirectRequest(userResponse, method)
        }
      

      這句代碼如果是3XX系列揪利,根據(jù)重定向的地址來重新構(gòu)建Request态兴,然后復(fù)制給request,在下一輪重新發(fā)起請求土童。
      除了以上兩種情況是需要重連的诗茎,之外的就是直接返回Response

    RetryAndFollowUpInterceptor的中置和后置我們看完了献汗,之后我們來仔細(xì)看下它的前置工作是干什么的敢订,我們點(diǎn)進(jìn)去:
    這是RealCall類中的一個(gè)函數(shù),也就是說所有的鏈?zhǔn)焦ぷ髌鋵?shí)都是在為RealCall來服務(wù)的罢吃,RealCall才是爸爸楚午,記住這點(diǎn),后面的代碼就很好理解了尿招。

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

    前面synchronized中的代碼都是對Request/Responsebody做檢測的矾柜,目的就是保證發(fā)起新的請求前,上一個(gè)body要關(guān)閉掉就谜,避免數(shù)據(jù)混亂怪蔑。
    后面的代碼才是關(guān)鍵。

    if (newExchangeFinder) {
        this.exchangeFinder = ExchangeFinder(
            connectionPool,
            createAddress(request.url),
            this,
            eventListener
        )
     }
    

    可以這樣理解丧荐,每一次請求缆瓣,都意味著需要一次數(shù)據(jù)交換,這個(gè)方法就是尋找一個(gè)數(shù)據(jù)交換虹统?弓坞?隧甚?啥意思?渡冻?戚扳?換個(gè)名詞來說,可能就會(huì)明白了族吻,尋找一個(gè)交換帽借,其實(shí)就是在尋找一個(gè)連接Connection,這樣是不是就明白了呼奢,就是為了尋找一個(gè)可用的TCP或者TLS的連接宜雀。但是呢, 其實(shí)這里并不是尋找連接,而是為尋找連接來做準(zhǔn)備的握础,就是準(zhǔn)備各種參數(shù)辐董,至于尋找連接,那是在其他Interceptor中來進(jìn)行的工作禀综。
    這樣我們就大致明白RetryAndFollowUpInterceptor的工作內(nèi)容了:

    • 連接準(zhǔn)備
    • 請求
    • 處理重連

到這里RetryAndFollowUpInterceptor代碼就看完了简烘。
接下來看下一個(gè)Interceptor,BridgeInterceptor定枷。
BridgeInterceptor橋接Interceptor孤澎??欠窒?干啥用的覆旭?我們點(diǎn)進(jìn)去看下就明白了。

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

代碼很多岖妄,但是我們大致一看就知道是干什么的了型将。大致的工作就是為上一步準(zhǔn)備的請求和下一步即將發(fā)送的請求做一個(gè)連接。
其實(shí)說白了就是為我們添加各種必要但對于開發(fā)者來說比較麻煩的各種Header信息荐虐,所以一些必要且麻煩的HeaderOkHttp幫我們做了七兜。比如:Content-Length,Content-Encoding等各種Header福扬。還是老規(guī)矩腕铸,來分割下前、中铛碑、后置工作狠裹。

  • 前置工作
    添加各種Header。
  • 中置工作
    發(fā)起請求汽烦。
  • 后置工作
    因?yàn)榍爸霉ぷ鲙臀覀兲砑恿艘恍ゞzip Header酪耳,所以后置工作需要幫我們進(jìn)行解壓縮。

這個(gè)BridgeInterceptor相對來收是比較簡單的,接下來我們看下一個(gè)Interceptor碗暗。

CacheInterceptor,顧名思義就是做緩存工作用的Interceptor梢夯。請求之前先看下是否有可用緩存言疗,如果有,就直接用緩存颂砸,如果沒有則發(fā)起請求噪奄,請求結(jié)束后判斷是否可以緩存,如果可以則進(jìn)行緩存人乓,這就是它大概的一個(gè)工作內(nèi)容勤篮,我們仔細(xì)想一下就可以想明白的。

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

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

額色罚。碰缔。。挺多代碼的戳护。還是老規(guī)矩金抡,分割前、中腌且、后置工作梗肝。

  • 前置工作
    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)
    }
    
    檢查是否有可用緩存,如果有則直接返回緩存內(nèi)容铺董,代碼這里就不分析了巫击,比較簡單。
  • 中置工作
    networkResponse = chain.proceed(networkRequest)
    
    發(fā)起請求精续。
  • 后置工作
    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.
        }
      }
    }
    
    根據(jù)Response來判斷是否需要緩存坝锰,同樣代碼也是很簡單,這里就不細(xì)看了驻右。
    但是這里我們可能需要簡單了解下CacheStrategy什黑,緩存策略,其實(shí)就是根據(jù)不同的緩存Header來進(jìn)行不同的緩存策略堪夭。比如:Date愕把,Expires,Last-Modified森爽,ETag恨豁,Age。然后再結(jié)合ReponseHeader來執(zhí)行具體的緩存策略爬迟。比如:If-None-Match橘蜜,If-Modified-Since 。這里就不細(xì)看了,都比較簡單计福,接下來進(jìn)入下一個(gè)Interceptor跌捆。

ConnectInterceptor,連接Interceptor象颖,這個(gè)是重點(diǎn)佩厚,這個(gè)是重點(diǎn),這個(gè)是重點(diǎn)说订。

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

哇~代碼很少啊抄瓦,只有四行代碼。啪啪啪 ~~~ 別看代碼少陶冷,但是卻很難懂钙姊,硬核的東西比較多。
老規(guī)矩埂伦,分割前煞额、中、后置工作赤屋。

  • 前置工作
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.call.initExchange(chain)
    val connectedChain = realChain.copy(exchange = exchange)
    
  • 中置工作
    connectedChain.proceed(realChain.request)
    
  • 后置工作立镶,無,卒~类早。
    啊媚媒,為什么沒有后置工作,看名字涩僻,ConnectInterceptor缭召,它是建立連接的Interceptor,連接建立逆日,請求嵌巷,返回結(jié)果,還要什么后置工作室抽。

接下來我們具體分析代碼搪哪。
我們先來看下前置工作;

val exchange = realChain.call.initExchange(chain)

然后我們點(diǎn)進(jìn)去看下代碼:

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
}

呦呵~又是RealCall類中的一個(gè)函數(shù)坪圾,RealCall果然是爸爸晓折。
我們看下重點(diǎn)的代碼:

val codec = exchangeFinder.find(client, chain)
val result = Exchange(this, eventListener, exchangeFinder, codec)

第一句代碼:通過find找到一個(gè)codec,這個(gè)codec是干嘛用的兽泄?其實(shí)是coder&decoder縮寫漓概,字面意思,編碼解碼器病梢。哦原來是查找一個(gè)可用的編碼解碼器胃珍,就是找到一個(gè)針對不同的格式報(bào)文的編碼解碼器,比如:HTTP1版本的編碼解碼器,HTTP2的編碼解碼器觅彰。我們點(diǎn)擊去看下:

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

其實(shí)只有兩行代碼吩蔑,第一行找到一個(gè)健康的連接,根據(jù)這個(gè)連接來創(chuàng)建對應(yīng)的codec對象填抬。什么是一個(gè)健康的連接哥纫?我們接著在點(diǎn)進(jìn)去看下:

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

    // Confirm that the connection is good.
    if (candidate.isHealthy(doExtensiveHealthChecks)) {
      return candidate
    }

    // If it isn't, take it out of the pool.
    candidate.noNewExchanges()

    // Make sure we have some routes left to try. One example where we may exhaust all the routes
    // would happen if we made a new connection and it immediately is detected as unhealthy.
    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")
  }
}

兩部分代碼:第一部分找到一個(gè)連接,第二部分判斷連接是否是健康的痴奏。
我們從第一部分代碼點(diǎn)進(jìn)去看下是怎么樣找到一個(gè)連接的。

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

我k厌秒,這代碼著實(shí)有點(diǎn)長啊读拆。不慌,根據(jù)功能來劃分下鸵闪,慢慢分析檐晕。
第一部分:

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

先看下自己有沒有連接,如果有且是可重用的蚌讼,就直接返回辟灰。

第二部分:

if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
    val result = call.connection!!
    eventListener.connectionAcquired(call, result)
    return result
  }

從連接池中獲取一個(gè)可用的連接,此時(shí)傳入的參數(shù):(address, call, null, false)
說明下最后兩個(gè)參數(shù)分別是:路由和是否多路復(fù)用(HTTP2的特性)

這里說個(gè)題外話篡石,說明是路由芥喇?
一個(gè)路由包含3部分:proxy(代理類型:代理或者直連),ip以及port凰萨,也就是說一個(gè)路由如果確定之后继控,有可能發(fā)生變化的是proxy和ip。用一張圖來說明下胖眷,如下:


路由

我們點(diǎn)進(jìn)入看下這個(gè)allAcquirePooledConnection這個(gè)函數(shù):

  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
}

ok武通,代碼不多,主要是遍歷連接池中的連接珊搀,然后篩選連接冶忱,條件如下:

  1. 是否是需要多路復(fù)用的連接并且該鏈接是支持多路復(fù)用;
  2. 再看該該鏈接是否滿足要求:
    internal fun isEligible(address: Address, routes: List<Route>?): Boolean {
    assertThreadHoldsLock()
    
    // If this connection is not accepting new exchanges, we're done.
    if (calls.size >= allocationLimit || noNewExchanges) return false
    
    // If the non-host fields of the address don't overlap, we're done.
    if (!this.route.address.equalsNonHost(address)) return false  
    
    // If the host exactly matches, we're done: this connection can carry the address.
    if (address.url.host == this.route().address.url.host) {
      return true // This connection is a perfect match.
    }
    
    // At this point we don't have a hostname match. But we still be able to carry the
      request if
    // our connection coalescing requirements are met. See also:
    // https://hpbn.co/optimizing-application-delivery/#eliminate-domain-sharding
    // https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/
    
    // 1. This connection must be HTTP/2.
    if (http2Connection == null) return false
    
    // 2. The routes must share an IP address.
    if (routes == null || !routeMatchesAny(routes)) return false
    
    // 3. This connection's server certificate's must cover the new host.
    if (address.hostnameVerifier !== OkHostnameVerifier) return false
    if (!supportsUrl(address.url)) return false
    
    // 4. Certificate pinning must match the host.
    try {
      address.certificatePinner!!.check(address.url.host,   handshake()!!.peerCertificates)
    } catch (_: SSLPeerUnverifiedException) {
      return false
    }
    
    return true // The caller's address can be carried by this connection.
    }
    
    這段代碼主要是闡述步驟二滿足的標(biāo)準(zhǔn)是什么境析。
    • 當(dāng)前連接的數(shù)量小于最大限制且可以繼續(xù)創(chuàng)建新的Exechange

    • 當(dāng)前連接的地址和新連接的地址不相同囚枪,需滿足以下條件:

      • internal fun equalsNonHost(that: Address): Boolean {
            return this.dns == that.dns &&
            this.proxyAuthenticator == that.proxyAuthenticator &&
            this.protocols == that.protocols &&
            this.connectionSpecs == that.connectionSpecs &&
            this.proxySelector == that.proxySelector &&
            this.proxy == that.proxy &&
            this.sslSocketFactory == that.sslSocketFactory &&
            this.hostnameVerifier == that.hostnameVerifier &&
            this.certificatePinner == that.certificatePinner &&
            this.url.port == that.url.port
         }
        

        這里兩個(gè)地址是否相同,代碼已經(jīng)寫的很清楚了簿晓,這里就不在贅述眶拉。

    • 主機(jī)名是否相同

    • 是否為http2的連接

    • 判斷路由是否匹配

      •   private fun routeMatchesAny(candidates: List<Route>): Boolean {
              return candidates.any {
                it.proxy.type() == Proxy.Type.DIRECT &&
                    route.proxy.type() == Proxy.Type.DIRECT &&
                    route.socketAddress == it.socketAddress
              }
          }
        
    • 驗(yàn)證碼hostnameVerifier是否一致,這里驗(yàn)證的過程我們就不一一具體分析了憔儿。

    • 檢測certificatePinner忆植。
      如果以上條件都沒問題,也就是弄明白了該鏈接滿足了當(dāng)前的需求,繼續(xù)下一步:

    call.acquireConnectionNoEvents(connection)
    
    取得連接且不發(fā)送事件朝刊。

第三步:指定路由耀里,然后在拿一次不支持http2的了解;

 if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
    val result = call.connection!!
    eventListener.connectionAcquired(call, result)
    return result
  }

這個(gè)時(shí)候傳入的參數(shù)為:(address, call, routes, false)拾氓,可以看到這個(gè)時(shí)候已經(jīng)傳入了路由參數(shù)冯挎。這時(shí)候得到的連接就是從這一組路由中得到不支持http2的連接。
如果沒有得到連接咙鞍,進(jìn)入第四步房官;

第四步:創(chuàng)建一個(gè)新的連接

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

第五步:創(chuàng)建新的連接之后,再次沖連接池中再取一次续滋,而這次的入?yún)⒂肿兞耍?code>(address, call, routes, true)翰守,意思是只取路由中的支持多路服用的連接,如果取到了說明這組路由且支持多路復(fù)用的連接是存在連接池中的疲酌,然后從連接池中取出蜡峰,之后將剛剛新創(chuàng)建的連接關(guān)閉掉。
這里說明下為什么會(huì)出現(xiàn)這種情況呢朗恳?因?yàn)橥粫r(shí)間有可能同時(shí)發(fā)起多個(gè)請求湿颅,但是每個(gè)請求的host和schema是一樣的,只有path是不一樣的粥诫,這樣的請求是可以公用一個(gè)連接的油航,所以當(dāng)其中一個(gè)請求創(chuàng)建成功后,其他請求一定會(huì)拿到連接池中的連接臀脏,因?yàn)榈谝粋€(gè)創(chuàng)建成功后劝堪,會(huì)放入到連接池中,這個(gè)是同步的操作揉稚,當(dāng)放入到連接池之后秒啦,其他請求再去拿一定是有連接可用的。

這里額外說一下:nextRouteToTry搀玖,這個(gè)是干嘛用的呢余境?就是第四步和第五步的結(jié)合,第四步創(chuàng)建一個(gè)新的連接灌诅,既然能創(chuàng)建一個(gè)新的連接芳来,說明這個(gè)路由是可用的,當(dāng)出現(xiàn)錯(cuò)誤需要重連時(shí)猜拾,此時(shí)就不需要在重新查找路由了即舌,因?yàn)檫@個(gè)創(chuàng)建的連接對應(yīng)的路由是一定可用的,目的是為了加快連接速度挎袜。

經(jīng)過上述五步之后顽聂,一定會(huì)拿到連接肥惭,所以一共經(jīng)歷了五種獲取連接的情況。
然后findConnection函數(shù)就結(jié)束了紊搪,返回了一個(gè)連接蜜葱。然后判斷該鏈接是否健康,健康與否主要是有是否關(guān)閉來判斷的耀石。然后接著看返回之后的代碼牵囤。

 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)

在得到一個(gè)健康的連接之后,然后使用這個(gè)連接創(chuàng)建出一個(gè)編碼解碼器滞伟。
我們點(diǎn)進(jìn)去看下是怎樣創(chuàng)建編碼解碼器的:

@Throws(SocketException::class)
  internal fun newCodec(client: OkHttpClient, chain: RealInterceptorChain): ExchangeCodec {
    val socket = this.socket!!
    val source = this.source!!
    val sink = this.sink!!
    val http2Connection = this.http2Connection

    return if (http2Connection != null) {
      Http2ExchangeCodec(client, this, chain, http2Connection)
    } else {
      socket.soTimeout = chain.readTimeoutMillis()
      source.timeout().timeout(chain.readTimeoutMillis.toLong(), MILLISECONDS)
      sink.timeout().timeout(chain.writeTimeoutMillis.toLong(), MILLISECONDS)
      Http1ExchangeCodec(client, this, source, sink)
    }
  }

哦原來是根據(jù)連接的不同來創(chuàng)建不同協(xié)議的編碼解碼器揭鳞,如果是http1,就創(chuàng)建http1的編碼解碼器梆奈,如果是http2汹桦,就創(chuàng)建http2的編碼解碼器。ok鉴裹。接著往下走。
接著回溯代碼:

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
  }

可以看到在得到編碼解碼器之后钥弯,基于編碼解碼器來創(chuàng)建一個(gè)Exchange径荔。
來解釋下Codec和Exchange:

Codec是真正的發(fā)起請求和接受請求的,而Exchange是對Codec的包裝和代理脆霎,exchange中的讀寫數(shù)據(jù)总处,其實(shí)就是調(diào)用了codec中的讀寫數(shù)據(jù)。

好了睛蛛,接著網(wǎng)上回溯代碼鹦马,就會(huì)到了最開始initExchange函數(shù)入口的地方了:

 object ConnectInterceptor : Interceptor {
  @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)
  }
}

然后ConnectInterceptor進(jìn)行中置工作(交棒):

connectedChain.proceed(realChain.request)

ok,到這里整個(gè)ConnectInterceptor工作就分析完了忆肾,就是創(chuàng)建連接荸频,然后返回一個(gè)包裝好的Exchange,有了這個(gè)Exchange就可以做到發(fā)送和接受請求了客冈。接下來我們看最后一個(gè)Interceptor旭从, CallServerInterceptor,點(diǎn)擊去看下:

class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.exchange!!
    val request = realChain.request
    val requestBody = request.body
    val sentRequestMillis = System.currentTimeMillis()

    exchange.writeRequestHeaders(request)

    var invokeStartEvent = true
    var responseBuilder: Response.Builder? = null
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return
      // what we did get (such as a 4xx response) without ever transmitting the request body.
      if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
        exchange.flushRequest()
        responseBuilder = exchange.readResponseHeaders(expectContinue = true)
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
      if (responseBuilder == null) {
        if (requestBody.isDuplex()) {
          // Prepare a duplex body so that the application can send a request body later.
          exchange.flushRequest()
          val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
          requestBody.writeTo(bufferedRequestBody)
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
          requestBody.writeTo(bufferedRequestBody)
          bufferedRequestBody.close()
        }
      } else {
        exchange.noRequestBody()
        if (!exchange.connection.isMultiplexed) {
          // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
          // from being reused. Otherwise we're still obligated to transmit the request body to
          // leave the connection in a consistent state.
          exchange.noNewExchangesOnConnection()
        }
      }
    } else {
      exchange.noRequestBody()
    }

    if (requestBody == null || !requestBody.isDuplex()) {
      exchange.finishRequest()
    }
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
    }
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    var code = response.code
    if (code == 100) {
      // Server sent a 100-continue even though we did not request one. Try again to read the actual
      // response status.
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
      }
      response = responseBuilder
          .request(request)
          .handshake(exchange.connection.handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build()
      code = response.code
    }

    exchange.responseHeadersEnd(response)

    response = if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response.newBuilder()
          .body(EMPTY_RESPONSE)
          .build()
    } else {
      response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()
    }
    if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
        "close".equals(response.header("Connection"), ignoreCase = true)) {
      exchange.noNewExchangesOnConnection()
    }
    if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
      throw ProtocolException(
          "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
    }
    return response
  }
}

額场仲,又是一坨代碼和悦,不慌,點(diǎn)根煙安靜下渠缕,可以發(fā)現(xiàn)鸽素,這就是在利用Exchange來進(jìn)行寫請求和讀響應(yīng)啊,然后對響應(yīng)進(jìn)行處理亦鳞,將最終的響應(yīng)直接返回馍忽,這樣這個(gè)鏈?zhǔn)秸{(diào)用就開始往回走了棒坏。看下重點(diǎn)代碼:

exchange.writeRequestHeaders(request)

寫請求頭信息

exchange.flushRequest()

結(jié)束請求寫操作舵匾。

exchange.readResponseHeaders(expectContinue = false)

讀取響應(yīng)頭俊抵。

response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()

讀取響應(yīng)體,構(gòu)建成一個(gè)響應(yīng)體:Response坐梯,然后返回徽诲。
接下來的過程就很熟悉了:

  • networkInterceptors,如果設(shè)置了自定義網(wǎng)絡(luò)攔截器吵血,會(huì)從這里進(jìn)行Response的回調(diào)谎替;
  • ConnectInterceptor,沒有后置工作蹋辅,直接返回Response钱贯;
  • CacheInterceptor,根據(jù)響應(yīng)頭來處理緩存侦另,之后返回Response秩命;
  • BridgeInterceptor,將響應(yīng)體進(jìn)行g(shù)zip解壓縮褒傅,然后返回Response弃锐;
  • RetryAndFollowUpInterceptor,根據(jù)響應(yīng)碼來判斷是否重連殿托,如不需要霹菊,返回Response
  • interceptors,如果用戶設(shè)置了自定的Interceptors支竹,這里會(huì)進(jìn)行Response的回調(diào)旋廷。

之后這個(gè)鏈?zhǔn)焦ぷ鹘Y(jié)束,回到getResponseWithInterceptorChain這個(gè)函數(shù)礼搁,還記得這個(gè)函數(shù)在哪調(diào)用的嗎饶碘?哈哈哈。馒吴。熊镣。對就是我們最開始分析鏈?zhǔn)焦ぷ鞯牡胤剑?br> AsyncCall中的run函數(shù)。

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

這之后就是responseCallback的各種回調(diào)了募书。
至此這個(gè)異步請求就結(jié)束了绪囱,整體下來也不是太難理解,還是很開心的莹捡。
至于RealCall中的execute函數(shù)(同步請求數(shù)據(jù))我們來簡單看下鬼吵;

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

哎呦我去,簡單粗暴篮赢,直接調(diào)用getResponseWithInterceptorChain函數(shù)齿椅,然后將結(jié)果直接返回琉挖。

好了,到這里整個(gè)OkHttp的源碼簡單解讀就結(jié)束涣脚,整體看來不是太難示辈,但也是需要多次進(jìn)行揣摩和分析的,相信對你來說小case啦遣蚀。加油~~~

個(gè)人能力有限矾麻,如有錯(cuò)誤之處,還望指出芭梯,我會(huì)第一時(shí)間驗(yàn)證并修改险耀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市玖喘,隨后出現(xiàn)的幾起案子甩牺,更是在濱河造成了極大的恐慌,老刑警劉巖累奈,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贬派,死亡現(xiàn)場離奇詭異,居然都是意外死亡澎媒,警方通過查閱死者的電腦和手機(jī)赠群,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旱幼,“玉大人,你說我怎么就攤上這事突委“芈保” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵匀油,是天一觀的道長缘缚。 經(jīng)常有香客問我,道長敌蚜,這世上最難降的妖魔是什么桥滨? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮弛车,結(jié)果婚禮上齐媒,老公的妹妹穿的比我還像新娘。我一直安慰自己纷跛,他們只是感情好喻括,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贫奠,像睡著了一般唬血。 火紅的嫁衣襯著肌膚如雪望蜡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天拷恨,我揣著相機(jī)與錄音脖律,去河邊找鬼。 笑死腕侄,一個(gè)胖子當(dāng)著我的面吹牛小泉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兜挨,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼膏孟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拌汇?” 一聲冷哼從身側(cè)響起柒桑,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎噪舀,沒想到半個(gè)月后魁淳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡与倡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年界逛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了毙石。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咏窿。...
    茶點(diǎn)故事閱讀 38,599評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖较幌,靈堂內(nèi)的尸體忽然破棺而出净响,到底是詐尸還是另有隱情少欺,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布馋贤,位于F島的核電站赞别,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏配乓。R本人自食惡果不足惜仿滔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望犹芹。 院中可真熱鬧崎页,春花似錦、人聲如沸腰埂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盐固。三九已至荒给,卻和暖如春丈挟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背志电。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工曙咽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挑辆。 一個(gè)月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓例朱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鱼蝉。 傳聞我的和親對象是個(gè)殘疾皇子洒嗤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容