Okhttp攔截器實(shí)現(xiàn)原理

分析的okhttp版本為

 implementation("com.squareup.okhttp3:okhttp:4.9.1")

RealCall.kt文件中 有一個(gè)函數(shù) fun getResponseWithInterceptorChain():Response 這個(gè)函數(shù)中是攔截器相關(guān)邏輯的開(kāi)始。

  @Throws(IOException::class)
  internal fun getResponseWithInterceptorChain(): Response {
    // 構(gòu)建一個(gè)攔截器可變列表,添加全部攔截器到這個(gè)列表
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors //用戶自己定義的攔截器
    //以下全部是okhttp內(nèi)置攔截器  
    interceptors += RetryAndFollowUpInterceptor(client)
    interceptors += BridgeInterceptor(client.cookieJar)
    interceptors += CacheInterceptor(client.cache)
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
      interceptors += client.networkInterceptors
    }
    //真正發(fā)起請(qǐng)求的攔截器
    interceptors += CallServerInterceptor(forWebSocket)

    //構(gòu)建攔截器請(qǐng)求chain,注意此處為起始chain
    val chain = RealInterceptorChain(
        call = this,
        interceptors = interceptors, //全部攔截器列表
        index = 0,//需要處理的攔截器列表的index
        exchange = null,
        request = originalRequest,//起初構(gòu)建的 Request 對(duì)象
        connectTimeoutMillis = client.connectTimeoutMillis,
        readTimeoutMillis = client.readTimeoutMillis,
        writeTimeoutMillis = client.writeTimeoutMillis
    )

    var calledNoMoreExchanges = false
    try {
      //執(zhí)行攔截器
      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)
      }
    }
  }

通過(guò)RealInterceptorChain的構(gòu)造方法也大致可以推斷出它內(nèi)部所包含的對(duì)象數(shù)據(jù)和方法

//簡(jiǎn)寫(xiě),偽代碼
class RealInterceptorChain(
    allInterceptor:mutableListOf<Interceptor>,//全部的攔截器
    index:Int//當(dāng)前需要處理攔截器列表哪一個(gè)攔截器的index
    originalRequest:Request//原始的請(qǐng)求提對(duì)象
){
    //執(zhí)行
    fun proceed(r:Request){}
}

看一下proceed()做了什么

 @Throws(IOException::class)
  override fun proceed(request: Request): Response {
    
    //此處省略一部分代碼...
 
    //構(gòu)建下一個(gè)chain (RealInterceptorChain)
    val next = copy(index = index + 1, request = request)
    //取出當(dāng)下攔截器,注意下次這個(gè)index 會(huì)被+1
    val interceptor = interceptors[index]

    //通過(guò)攔截器調(diào)用intercept()并把對(duì)應(yīng)的chain傳入攔截器
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    //此處省去一部分代碼...
    
    return response //返回響應(yīng)體對(duì)象
  }

現(xiàn)在來(lái)看一張圖

攔截器調(diào)用原理.jpg

這張圖詮釋了攔截器的調(diào)用順序與內(nèi)存對(duì)象關(guān)系,簡(jiǎn)單梳理一下:

真正的調(diào)用是第一個(gè)RealInterceptorChain對(duì)象發(fā)起的,通過(guò)它的proceed()方法,構(gòu)建出一個(gè)Chain對(duì)象,并找到一個(gè)攔截器,把這個(gè)chain對(duì)象傳遞給這個(gè)攔截器,通過(guò)攔截器中的chain再次調(diào)用proceed()重復(fù)上面的動(dòng)作。
直到CallServerInterceptor攔截器為止開(kāi)始返回Response對(duì)象,之后就是攔截器中的chain.proceed(req)后面的調(diào)用被執(zhí)行(遞歸)。

通過(guò)了解了攔截器的實(shí)現(xiàn)機(jī)制,我們?cè)诙x攔截器時(shí),就可以毫不費(fèi)力了。

比如如下定義的攔截器:

/**
* 緩存策略攔截器
*/
class CacheInterceptor(var day: Int = 7) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()//獲取RealInterceptorChain中的request對(duì)象
        if (!NetworkUtil.isNetworkAvailable(appContext)) {
            request = request.newBuilder()
                .cacheControl(CacheControl.FORCE_CACHE)
                .build()
        }
        val response = chain.proceed(request)//執(zhí)行RealInterceptorChain中的proceed()繼而達(dá)到調(diào)用下一個(gè)攔截器的目的
        
        //以下都是在CallServerInterceptor攔截器返回后被調(diào)用
        if (!NetworkUtil.isNetworkAvailable(appContext)) {
            val maxAge = 60 * 60
            response.newBuilder()
                .removeHeader("Pragma")
                .header("Cache-Control", "public, max-age=$maxAge")
                .build()
        } else {
            val maxStale = 60 * 60 * 24 * day // tolerate 4-weeks stale
            response.newBuilder()
                .removeHeader("Pragma")
                .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale")
                .build()
        }
        return response
    }
}

我們看到自定義攔截器最關(guān)心的兩個(gè)方法是

var request = chain.request() //就是獲取chain中保存的request對(duì)象
val response = chain.proceed(request) //執(zhí)行下一個(gè)攔截器,直到CallServerInterceptor返回

我們?cè)诤?jiǎn)單看一下CallServerInterceptor

class CallServerInterceptor{
    
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
  
    val request = realChain.request
    val requestBody = request.body
    requestBody.writeTo(bufferedRequestBody) //真正使用request對(duì)象
 
    var response = responseBuilder//真正創(chuàng)建 response 對(duì)象
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
   
    return response
  }
}

到此 攔截器分析完畢!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Hello小伙伴們,現(xiàn)在公司已經(jīng)恢復(fù)了正常辦公,但是疫情依舊還在繼續(xù)。最近工作實(shí)在是有點(diǎn)小忙,導(dǎo)致更新有點(diǎn)緩慢,實(shí)...
    醬爆大頭菜閱讀 3,957評(píng)論 6 5
  • OkHttp3源碼解析(一)分發(fā)器Dispatcher原理分析OkHttp3源碼解析(二)五大攔截器原理分析 從上...
    程序員三千_閱讀 1,148評(píng)論 0 7
  • 在 Okhttp源碼學(xué)習(xí)一(基本請(qǐng)求流程)中,只是學(xué)習(xí)了okhttp請(qǐng)求網(wǎng)絡(luò)的一個(gè)基本流程,但是最關(guān)鍵的點(diǎn),同步或...
    有興不虛昧閱讀 525評(píng)論 0 1
  • 攔截器官方定義 攔截器是OkHttp中提供的一種強(qiáng)大機(jī)制,它可以實(shí)現(xiàn)網(wǎng)絡(luò)監(jiān)聽(tīng)、請(qǐng)求以及響應(yīng)重寫(xiě)、請(qǐng)求失敗重試等功能...
    dlihasa閱讀 934評(píng)論 0 1
  • 上一篇我們說(shuō)完了dispatcher分發(fā)器,我們知道了請(qǐng)求任務(wù)是如何分發(fā)出去的,那響應(yīng)是如何獲取到的呢?再看一下R...
    Thisislife閱讀 1,032評(píng)論 0 1

友情鏈接更多精彩內(nèi)容