讀源碼-OkHttp源碼解析


本文基於OkHttp版本,該版本是用Kotlin實現:

com.squareup.okhttp3:okhttp:4.7.2

1-基本流程

OkHttp這個框架就不用過多介紹了吧,一句話概括就是對Socket編程的封裝實現,方便實現網絡通信。什麼是Socket編程?Socket是TCP/IP協議的抽象實現,詳情可以去看看:一篇文章帶你熟悉 TCP/IP 協議-(一)

String testUrl = "https://wanandroid.com/wxarticle/chapters/json";
//@1.初始化OkHttpClient
OkHttpClient client = new OkHttpClient();
//@2.根據url構建Request
final Request request = new Request.Builder().url(testUrl).build();
//@3.發起同步請求
Response syncResponse = client.newCall(request).execute();
//@4.發起異步請求
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(@NotNull Call call, @NotNull IOException e) {//TODO 失敗回調 }
    @Override
    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {//TODO 成功回調}
});

@1.第一步當然是初始化工作了,通過Builder模式構建OkHttpClient。主要是初始化一些基本參數配置,包括任務調度器、連接池、攔截器、線程池等。後面會詳細講述這些內容。核心是任務調度器和攔截器,任務調度器Dispatcher主要是維護了三個任務隊列:

  • readyAsyncCalls = ArrayDeque()
    異步方式待執行的任務隊列
  • runningAsyncCalls = ArrayDeque()
    異步方式正在執行的任務隊列
  • runningSyncCalls = ArrayDeque()
    同步方式執行的任務隊列

攔截器機制後面將作爲重點講述。

@2.第二步開始構造請求內容。根據Http協議我們知道發起網絡請求的報文協議是請求行+請求頭+請求體。這一步就是通過Builder模式將http協議報文的必要信息封裝到一個Request對象,後面會將該Request對象轉爲符合http協議的報文發起一個網絡請求。

internal constructor(request: Request) {
  this.url = request.url//將字符串url轉換成HttpUrl對象,便於提取Url中的scheme、path等信息
  this.method = request.method//請求方法post、get等
  this.body = request.body//請求體,post方法需要將內容放在請求體
  this.tags = if (request.tags.isEmpty()) {
    mutableMapOf()
  } else {
    request.tags.toMutableMap()
  }
  //請求頭,這裏是用數組實現,
  //key、value、key、value這樣存儲鍵值對,所以肯定是偶數個
  this.headers = request.headers.newBuilder()
}

@3、@4本質上原理是一樣的,只是一個是同步實現一個是異步實現,同步異步不做多解釋,我們就看看同步請求的實現來了解其原理。client.newCall(request).execute()這裏就是通過傳入request,創建了一個Call的實現類RealCall的對象,並調用其execute方法執行請求任務,執行完畢返回一個Resonse對象。

override fun execute(): Response {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    timeout.enter()
    callStart()
    try {
        //將請求任務添加到dispatcher的runningSyncCalls隊列
      client.dispatcher.executed(this)
        //@5.通過攔截器鏈一步步處理請求任務
      return getResponseWithInterceptorChain()
    } finally {
        //任務執行完畢從runningSyncCalls隊列移除該任務
      client.dispatcher.finished(this)
    }
}

@5.通過攔截器鏈一步步處理請求任務。這裏就是OkHttp框架的精髓了,將網絡請求Request通過一個InterceptorChain加工處理生產出請求結果Response。InterceptorChain就像一條流水生產線,鏈式調用、分工明確。直接看代碼吧:

@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
    val interceptors = mutableListOf<Interceptor>()
    //(1)用戶自定義頂層全局攔截器,優先級最高
    interceptors += client.interceptors
    //(2)錯誤、重定向攔截器
    interceptors += RetryAndFollowUpInterceptor(client)
    //(3)橋接攔截器,橋接應用層與網絡層,添加必要的頭信息
    interceptors += BridgeInterceptor(client.cookieJar)
    //(4)緩存處理攔截器
    interceptors += CacheInterceptor(client.cache)
    //(5)連接攔截器。建立C與S的Socket連接
    interceptors += ConnectInterceptor
    //(6)用戶自定義的NetworkInterceptor
    if (!forWebSocket) {
      interceptors += client.networkInterceptors
    }
    //(7)訪問服務端攔截器。
    interceptors += CallServerInterceptor(forWebSocket)
    //通過這些攔截器構造攔截器鏈RealInterceptorChain
    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 {
        //通過RealInterceptorChain按順序執行攔截器,最終得到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是如何一步步將Request對象加工成Response對象的,第二章再分析各Interceptor的原理。前面將這些攔截器按順序添加到了interceptors中,所以RealInterceptorChain.proceed方法就是從index=0開始執行對應攔截器的intercept方法,流程如下(第3章流程總結中有總的流程圖):

  • (1)用戶自定義的Interceptor(可選)。通過OkHttpClient.Builder().addInterceptor添加,實際應用中會在此流程中往request添加一些全局的業務參數,比如cuid、userId、位置信息等
  • (2)RetryAndFollowUpInterceptor。開啓無限循環,執行後續攔截器步驟,根據返回的response
    • 不需要重試或重定向或者不能再重試、重定向,直接返回response跳出循環
    • 重試或重定向,更新request並再次執行後續步驟
  • (3)BridgeInterceptor。
    • 請求前,將Request對象轉換爲包含http協議信息的請求
    • 執行後續攔截器
    • 請求後,將返回結果Gzip解壓出響應體內容
  • (4)CacheInterceptor。
    • 請求前,判斷緩存策略,強制緩存則直接返回緩存,不再發起網絡請求
    • 執行後續攔截器
    • 請求後,對比緩存策略則使用緩存,否則使用網絡返回response,更新緩存
  • (5)ConnectInterceptor
    • 判斷當前連接是否可用,不可用則從連接池中獲取連接
    • 獲取失敗則新建連接並執行TCP三次握手建立連接
    • 執行後續流程
  • (6)NetworkInterceptor(可選)。在建立連接後–>正式發送請求前的過程,用戶進行一些處理。
  • (7)CallServerIntercepto
    • 請求前,將請求request encode爲http協議的報文
    • 請求後,從HTTP響應報文decode出response

2-攔截器

2.1-RetryAndFollowUpInterceptor

經歷了用戶自定義的Interceptor,第一個攔截器就是RetryAndFollowUpInterceptor,該攔截器主要處理重試和重定向,所以其實現是遞歸調用RealInterceptorChain.proceed方法執行後續步驟,根據遞歸結果判斷是否需要重試/重定向。來看代碼:

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
    。。。//代碼省略
    //開啓循環
    while (true) {
      call.enterNetworkInterceptorExchange(request, newExchangeFinder)
    
      var response: Response
      var closeActiveExchange = true
      try {
        if (call.isCanceled()) {//請求取消
          throw IOException("Canceled")
        }
        try {
            //遞歸調用,執行後續Interceptor步驟
          response = realChain.proceed(request)
          newExchangeFinder = true
        } catch (e: RouteException) {
          // @6.連接錯誤,判斷是否可以重試
          if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
            //不能重試,拋出異常
            throw e.firstConnectException.withSuppressed(recoveredFailures)
          } else {
            recoveredFailures += e.firstConnectException
          }
          newExchangeFinder = false
          //重試
          continue
        } catch (e: IOException) {
          // IO錯誤,判斷是否可以重試
          if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
            throw e.withSuppressed(recoveredFailures)
          } else {
            recoveredFailures += e
          }
          newExchangeFinder = false
          //重試
          continue
        }
    
        // 將上一次response合併,一般上一次回覆爲重定向response,無響應體
        if (priorResponse != null) {
          response = response.newBuilder()
              .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
              .build()
        }
    
        val exchange = call.interceptorScopedExchange
        //根據response的code判斷是否需要重定向
        //將response中的重定向信息封裝成重定向Request
        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
        //緩存當前response
        priorResponse = response
      } finally {
        call.exitNetworkInterceptorExchange(closeActiveExchange)
      }
    }
}

@6.recover方法判斷是否可以重試,返回false則不允許重試。

private fun recover(
    e: IOException,
    call: RealCall,
    userRequest: Request,
    requestSendStarted: Boolean
  ): Boolean {
    // OkHttpClient設置了不允許重試
    if (!client.retryOnConnectionFailure) return false

    // StreamedRequestBody類型的請求只能執行一次,不允許重試
    if (requestSendStarted && requestIsOneShot(e, userRequest)) return false

    // 協議錯誤、安全問題不允許重試
    if (!isRecoverable(e, requestSendStarted)) return false

    // 無更多可以路由,不允許重試
    if (!call.retryAfterFailure()) return false

    return true
  }

源碼看下來RetryAndFollowUpInterceptor其實工作比較簡單,就是根據後面攔截器處理的結果來判斷是否需要重試、重定向,需要的話就再次執行後面的鏈路,不需要則跳出循環直接返回結果。

總結下RetryAndFollowUpInterceptor的處理流程:
在這裏插入圖片描述

2.2-BridgeInterceptor

BridgeInterceptor看名字就知道是建立網絡連接的橋樑,請求前將Response對象加工成http協議的request,請求後再解壓返回數據gzip,將數據封裝爲http協議的response。來看代碼:

override fun intercept(chain: Interceptor.Chain): Response {
    val userRequest = chain.request()
    //構建http請求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())
    }
    。。。//代碼省略,沒什麼好說的,就是構建http請求頭部分的鍵值對
    
    //遞歸調用後面的攔截器,獲取返回的結果
    val networkResponse = chain.proceed(requestBuilder.build())
    //緩存cookie
    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
    //構建http響應response
    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)
    //gzip編碼方式
    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        networkResponse.promisesBody()) {
      val responseBody = networkResponse.body
      if (responseBody != null) {
        //解壓gzip,根據解壓出來的數據構建響應頭和響應體
        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()
}

BridgeInterceptor的職責也是相當明確,網絡請求前將response對象轉換爲http請求,網絡請求返回後再將結果封裝成http response

2.3-CacheInterceptor

CacheInterceptor主要作用就是處理緩存,同樣也分請求前和請求後兩部分。請求前判斷是否可以直接用緩存,請求後更新緩存。

這裏需要注意OkHttp的cache默認是未實現的,需要在構造OkHttpClient時設置Cache,Cache是通過封裝DiskLruCache來管理緩存文件。

//定義緩存路徑及緩存閾值
Cache cache = new Cache(directory, maxSize);
new OkHttpClient.Builder().cache(cache).build();

再來看看CacheInterceptor的源碼:

override fun intercept(chain: Interceptor.Chain): Response {
    val call = chain.call()
    //根據rquest查找對應的緩存response作爲候選緩存cacheCandidate
    val cacheCandidate = cache?.get(chain.request())
    val now = System.currentTimeMillis()
    //@7.根據request和cacheCandidate決策採用哪種緩存策略
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    //緩存策略產生的有效緩存cacheResponse
    val cacheResponse = strategy.cacheResponse
    //記錄緩存命中情況
    cache?.trackResponse(strategy)
    val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
    //有效緩存爲空,關閉流
    if (cacheCandidate != null && cacheResponse == null) {
      cacheCandidate.body?.closeQuietly()
    }
    // 無網絡且無有效緩存,組裝並返回504 Response
    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 (networkRequest == null) {
      return cacheResponse!!.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build().also {
            listener.cacheHit(call, it)
          }
    }
    //緩存命中/miss回調
    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 (networkResponse == null && cacheCandidate != null) {
        cacheCandidate.body?.closeQuietly()
      }
    }

    //對比緩存策略
    if (cacheResponse != null) {
        //服務端返回304 NotModified表明緩存可用
        //緩存封裝並返回
      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()

        cache!!.trackConditionalCacheHit()
        //response更新緩存cacheResponse
        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) {
        //根據Http響應狀態碼及是否有body判斷是否可以緩存
      if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
        //將networkResponse緩存
        val cacheRequest = cache.put(response)
        return cacheWritingResponse(cacheRequest, response).also {
          if (cacheResponse != null) {
            // This will log a conditional cache miss only.
            listener.cacheMiss(call)
          }
        }
      }
        //根據請求方法判斷是否爲非法緩存
        //POST/PATCH/PUT/DELETE/MOVE方法不緩存
      if (HttpMethod.invalidatesCache(networkRequest.method)) {
        try {
          cache.remove(networkRequest)
        } catch (_: IOException) {
        }
      }
    }
    return response
}

@7.根據request和cacheCandidate決策採用哪種緩存策略。關鍵代碼
CacheStrategy.compute–>CacheStrategy.computeCandidate,代碼內容較長就不貼出來了,這裏會決策出兩種緩存策略:

  • 強制緩存,即緩存在有效期就直接返回該緩存,不再進行網絡請求
  • 對比緩存,緩存超過有效期,進行網絡請求。若服務端返回304 NotModified表示和上次請求對比數據內容並未修改,則該緩存仍然有效;若服務端正常響應則返回服務端數據。

總結下CacheInterceptor的流程:
在這裏插入圖片描述

2.4-ConnectInterceptor

ConnectInterceptor看名字就知道是處理網絡連接的攔截器,源碼很少,職責就是通過Socket建立連接,優先使用已存在的connection,其次從CoonectionPool中獲取connection並connect,最後都沒有則新建connection並connect

object ConnectInterceptor : Interceptor {
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    //@8.核心,從連接池connectionPool查找可用連接或新建連接
    //來傳送request或接收response
    val exchange = realChain.call.initExchange(chain)
    val connectedChain = realChain.copy(exchange = exchange)
    return connectedChain.proceed(realChain.request)
  }
}

@8.調用鏈路ConnectionInterceptor.intercept–>RealCall.initExchange–>ExchangeFinder.find

fun find(
    client: OkHttpClient,
    chain: RealInterceptorChain
  ): ExchangeCodec {
    try {
    //@9.查找可用的連接
      val resultConnection = findHealthyConnection(
          connectTimeout = chain.connectTimeoutMillis,
          readTimeout = chain.readTimeoutMillis,
          writeTimeout = chain.writeTimeoutMillis,
          pingIntervalMillis = client.pingIntervalMillis,
          connectionRetryEnabled = client.retryOnConnectionFailure,
          doExtensiveHealthChecks = chain.request.method != "GET"
      )
      //將相關連接信息封裝成Http2ExchangeCodec/Http1ExchangeCodec返回
      //以便下一個攔截器CallServerInterceptor開始進行通信
      return resultConnection.newCodec(client, chain)
    } catch (e: RouteException) {
      trackFailure(e.lastConnectException)
      throw e
    } catch (e: IOException) {
      trackFailure(e)
      throw RouteException(e)
    }
}

@9.查找可用連接,通過循環調用findConnection找到連接,優先查找當前連接,其次連接池中的connection,最後新建連接。如果連接可用則跳出循環。看下其核心方法findConnection

  //如果該連接不可用,findHealthyConnection會移除該連接
  //並重新進入findConnection方法查找
private fun findHealthyConnection(。。。//形參省略): RealConnection {
    while (true) {
    //@10.查找連接,優先當前連接,其次連接池,最後新建連接
      val candidate = findConnection(。。。//實參省略)

      // 連接可用則直接返回該連接,跳出循環
      if (candidate.isHealthy(doExtensiveHealthChecks)) {
        return candidate
      }
      // 不可用則移除該連接,繼續循環調用findConnection查找
      candidate.noNewExchanges()

      。。。//代碼省略
    }
}

@10.查找連接,優先當前連接,其次連接池,最後新建連接

private fun findConnection(。。。//形參省略): RealConnection {
    。。。//省略
    synchronized(connectionPool) {
      。。。//省略
      //如果當前已建立連接,直接返回
      if (call.connection != null) {
        result = call.connection
        releasedConnection = null
      }
      if (result == null) {
        refusedStreamCount = 0
        connectionShutdownCount = 0
        otherFailureCount = 0
        //當前連接不可用,從連接池中通過host和port查找連接
        if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
            //連接池中找到緩存,標記
          foundPooledConnection = true
          result = call.connection
        } else if (nextRouteToTry != null) {
            //未找到,且還有待查找路由,則更新路由
            //便於下面到這個路由的連接池查找
          selectedRoute = nextRouteToTry
          nextRouteToTry = null
        }
      }
    }
    toClose?.closeQuietly()
    if (releasedConnection != null) {
      eventListener.connectionReleased(call, releasedConnection!!)
    }
    if (foundPooledConnection) {
      eventListener.connectionAcquired(call, result!!)
    }
    //返回當前連接or緩存池中找到的連接
    if (result != null) {
      return result!!
    }

    //如果需要切換路由查找,則切換路由,阻塞操作
    var newRouteSelection = false
    if (selectedRoute == null && (routeSelection == null || !routeSelection!!.hasNext())) {
      var localRouteSelector = routeSelector
      if (localRouteSelector == null) {
        localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
        this.routeSelector = localRouteSelector
      }
      newRouteSelection = true
      routeSelection = localRouteSelector.next()
    }

    var routes: List<Route>? = null
    synchronized(connectionPool) {
      if (call.isCanceled()) throw IOException("Canceled")

      if (newRouteSelection) {
        //如果切換路由了,則在新路由中查找
        routes = routeSelection!!.routes
        if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
          foundPooledConnection = true
          result = call.connection
        }
      }
        
      if (!foundPooledConnection) {
        if (selectedRoute == null) {
          selectedRoute = routeSelection!!.next()
        }

        // 連接池中未找到則創建新的連接 
        result = RealConnection(connectionPool, selectedRoute!!)
        connectingConnection = result
      }
    }

    // 如果第二次連接池查找成功,直接返回連接
    if (foundPooledConnection) {
      eventListener.connectionAcquired(call, result!!)
      return result!!
    }

    // 連接池查找失敗,調用RealConnection.connect建立連接
    // 主要就是TCP中的三次握手建立連接
    result!!.connect(
        connectTimeout,
        readTimeout,
        writeTimeout,
        pingIntervalMillis,
        connectionRetryEnabled,
        call,
        eventListener
    )
    call.client.routeDatabase.connected(result!!.route())

    var socket: Socket? = null
    synchronized(connectionPool) {
      connectingConnection = null
      //多路複用則進行合併
      if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) { 
        result!!.noNewExchanges = true
        socket = result!!.socket()
        result = call.connection
        // 剛創建的連接不可用,在該路由中重試
        nextRouteToTry = selectedRoute
      } else {
        //將連接加入連接池
        connectionPool.put(result!!)
        call.acquireConnectionNoEvents(result!!)
      }
    }
    socket?.closeQuietly()
    eventListener.connectionAcquired(call, result!!)
    return result!!
}

總結下ConnectInterceptor的工作流程:
在這裏插入圖片描述

2.5-CallServerInterceptor

在BridgeInterceptor環節中將Request對象轉換爲包含http協議的請求對象,但該對象還需要進一步轉換成我們熟悉的http報文才能進行發起Http請求。先回顧下Http的請求報文:

在這裏插入圖片描述

所以CallServerInterceptor在發起請求前,將request對象encode爲http請求報文。http響應報文decode成response對象

override fun intercept(chain: Interceptor.Chain): Response {
    。。。//代碼省略
    //寫入請求頭
    exchange.writeRequestHeaders(request)

    var invokeStartEvent = true
    var responseBuilder: Response.Builder? = null
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      // 當Header爲Expect: 100-continue時,只發送請求頭
      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) {
          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()

    。。。//省略
    return response
}

具體的WebSocket建立連接的過程比較複雜篇幅太長,涉及到WebSocket、Source、Sink、okio等內容,可參考其他博文。

3-流程總結

在這裏插入圖片描述

  • (1)用戶自定義的Interceptor(可選)。通過OkHttpClient.Builder().addInterceptor添加,實際應用中會在此流程中往request添加一些全局的業務參數,比如cuid、userId、位置信息等
  • (2)RetryAndFollowUpInterceptor。開啓無限循環,執行後續攔截器步驟,根據返回的response
    • 不需要重試或重定向或者不能再重試、重定向,直接返回response跳出循環
    • 重試或重定向,更新request並再次執行後續步驟
  • (3)BridgeInterceptor。
    • 請求前,將Request對象轉換爲包含http協議信息的請求
    • 執行後續攔截器
    • 請求後,將返回結果Gzip解壓出響應體內容
  • (4)CacheInterceptor。
    • 請求前,判斷緩存策略,強制緩存則直接返回緩存,不再發起網絡請求
    • 執行後續攔截器
    • 請求後,對比緩存策略則使用緩存,否則使用網絡返回response,更新緩存
  • (5)ConnectInterceptor
    • 判斷當前連接是否可用,不可用則從連接池中獲取連接
    • 獲取失敗則新建連接並執行TCP三次握手建立連接
    • 執行後續流程
  • (6)NetworkInterceptor(可選)。在建立連接後–>正式發送請求前的過程,用戶進行一些處理。
  • (7)CallServerIntercepto
    • 請求前,將請求request encode爲http協議的報文
    • 請求後,從HTTP響應報文decode出response
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章