筆記--OkHttp攔截器

今天捋清楚了OkHttp的攔截器機(jī)制,并且理解之后試著模仿著寫(xiě)了一套攔截器的架構(gòu)。把筆記記下來(lái),方便以后查看。

OkHttp攔截器

用OkHttp做網(wǎng)絡(luò)請(qǐng)求的時(shí)候,可以很方便的根據(jù)自身需要構(gòu)造很多攔截器,以實(shí)現(xiàn)不同的功能,對(duì)請(qǐng)求體和返回信息進(jìn)行處理。甚至可能這個(gè)機(jī)制太好用了,連OkHttp本身最后對(duì)服務(wù)器的請(qǐng)求也是通過(guò)定義一個(gè)攔截器來(lái)實(shí)現(xiàn)的,這個(gè)攔截器叫CallServerInterceptor,是攔截器鏈條里面的最后一個(gè)攔截器。
我們來(lái)看一個(gè)基本的網(wǎng)絡(luò)請(qǐng)求,它的流程很簡(jiǎn)單:請(qǐng)求,等待響應(yīng),拿到響應(yīng)信息,結(jié)束。
大致如下圖:


基本網(wǎng)絡(luò)請(qǐng)求流程

但是很多時(shí)候我們有需求在這個(gè)流程中做很多自己需要的操作,比如打印日志啦,添加統(tǒng)一的請(qǐng)求消息頭啦,根據(jù)返回信息的共同特征做特定的統(tǒng)一操作啦(比如token過(guò)期跳登錄頁(yè)面),還有序列化對(duì)象啦等等等等很多各式各樣的需求。這個(gè)時(shí)候攔截器就派上用場(chǎng)了,它的功能極其強(qiáng)大,基本可以滿足你在從請(qǐng)求到返回響應(yīng)過(guò)程中想做的一切騷操作。
我們看看加入攔截器之后流程的變化:


添加一個(gè)攔截器

我們捋一捋攔截器做的這幾件事

[1] 首先,攔截器接收一個(gè)request,拿到這個(gè)request之后,可以決定要不要做加工處理,然后把加工好的request交給下一個(gè)攔截器,這是第一件事。
[2] 自己處理完了之后,要驅(qū)動(dòng)下一個(gè)攔截器執(zhí)行它的操作,不能一層一層處理到自己這里就停下來(lái)了,導(dǎo)致鏈條運(yùn)行中斷,這一步必須做。這是第二件事。
[3] 處理完request,還要看看下一層返回來(lái)的response自己要不要處理一下,加工好了之后再把它丟給上一層。這是第三件事。
[4] 第四件事其實(shí)包含在1和3里面,就是根據(jù)上一層傳下來(lái)的request和下一層返回的response信息決定要不要做額外的操作,這個(gè)其實(shí)是附帶的操作,和整體攔截器的運(yùn)行流程相關(guān)性不大。

根據(jù)上面幾條,依次來(lái)看,我們來(lái)看看實(shí)現(xiàn)一個(gè)攔截器要實(shí)現(xiàn)的基本方法:

class InterceptorA implements Interceptor{
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            //處理requeset...
            Response response = chain.proceed(request);
            //處理response...
            return response;
        }
    }

實(shí)現(xiàn)攔截器需要實(shí)現(xiàn)一個(gè)方法,就是intercept方法,但是傳入的參數(shù)卻不是Request,而是一個(gè)Chain對(duì)象,這個(gè)對(duì)象,是一個(gè)靈魂中樞。
通過(guò)上面的描述,我們可以知道,攔截器機(jī)制其實(shí)是通過(guò)責(zé)任鏈模式來(lái)實(shí)現(xiàn)的,所以這個(gè)靈魂中樞對(duì)象起名為Chain(鏈條)。
Chain里面保存了傳入的Request對(duì)象,可以隨時(shí)取出來(lái)進(jìn)行加工處理。
Chain里面保存了整個(gè)的攔截器列表,可以取出下一個(gè)攔截器,執(zhí)行intercept方法,也因此,Chain可以從下一個(gè)攔截器的intercept方法中獲取下一層返回回來(lái)的Response,然后對(duì)Response進(jìn)行加工處理,再返回給上一層。
這就是上面的攔截器要做的三件事,都依賴(lài)Chain這個(gè)對(duì)象。
然后我們來(lái)看看Chain的實(shí)現(xiàn),看看它都做了那些事...
我們知道,責(zé)任鏈模式中每一個(gè)鏈塊執(zhí)行完自己的操作之后都要繼續(xù)驅(qū)動(dòng)下一個(gè)鏈塊繼續(xù)執(zhí)行操作,這就要求每一個(gè)鏈塊需要保存有下一個(gè)鏈塊的執(zhí)行方法入口,有的人通過(guò)一個(gè)公共的管理方法來(lái)驅(qū)動(dòng)執(zhí)行,也有的直接保存下一個(gè)鏈塊的對(duì)象,攔截器這里的處理方式是,讓每一個(gè)鏈塊都持有了整個(gè)攔截器列表的實(shí)體,執(zhí)行完這一個(gè)之后,從列表中取出下一個(gè),繼續(xù)執(zhí)行。

/**
 * A concrete interceptor chain that carries the entire interceptor chain: all application
 * interceptors, the OkHttp core, all network interceptors, and finally the network caller.
 */
public final class RealInterceptorChain implements Interceptor.Chain {
  private final List<Interceptor> interceptors;//重點(diǎn)
  private final StreamAllocation streamAllocation;
  private final HttpCodec httpCodec;
  private final RealConnection connection;
  private final int index;//重點(diǎn)
  private final Request request;
  private final Call call;
  private final EventListener eventListener;
  private final int connectTimeout;
  private final int readTimeout;
  private final int writeTimeout;
  private int calls;
  ...
  ...
}

上面的 List<Interceptor> interceptors是整個(gè)所有的攔截器列表,index是當(dāng)前的執(zhí)行位置,根據(jù)index,可以從list中取出下一個(gè)有效的攔截器,繼續(xù)執(zhí)行。
它的構(gòu)造方法里有11個(gè)參數(shù):

public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
      EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
    this.call = call;
    this.eventListener = eventListener;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
  }

其中和攔截器運(yùn)行機(jī)制相關(guān)的關(guān)鍵參數(shù)有三個(gè):

List<Interceptor> interceptors;//完整的攔截器列表
int index;//當(dāng)前的執(zhí)行位置
Request request;//上一層傳入的Request

這就是它驅(qū)動(dòng)下一層攔截器繼續(xù)執(zhí)行所依賴(lài)的基本信息。
那么具體怎么取出下一個(gè)攔截器繼續(xù)執(zhí)行下一層的呢?
我們都知道,在實(shí)現(xiàn)Interceptor接口的時(shí)候,去實(shí)現(xiàn)它的intercept(Chain chain)方法,有一行代碼是必須要執(zhí)行的,那就是chain.proceed(request),哪怕你啥也不做,你也得寫(xiě)成下面這個(gè)樣子:

class InterceptorB implements Interceptor{
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            return chain.proceed(request);
        }
    }

為啥必須執(zhí)行這行代碼呢,因?yàn)榫褪撬?fù)責(zé)去驅(qū)動(dòng)下一層攔截器執(zhí)行操作,不寫(xiě)這行代碼,攔截器鏈條走到這一層,就斷了,整個(gè)鏈條的處理就會(huì)停在這里。
我們來(lái)看一下proceed方法的具體實(shí)現(xiàn):

@Override 
public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
 }
 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
  }

取出里面關(guān)鍵的代碼出來(lái)看:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    ...
    ...
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    ...
    ...
}

上面的注釋也寫(xiě)的很清晰了:調(diào)用鏈條中的下一個(gè)攔截器。第一行代碼,構(gòu)造下一個(gè)攔截器的Chain,傳入本層的request,index+1,以及攔截器列表。然后從攔截器列表中取出下一個(gè)攔截器,傳入構(gòu)造好的Chain對(duì)象,執(zhí)行intercept(chain)方法。下一層的方法處理完,返回response,本層通過(guò)proceed()方法取得response,再?zèng)Q定要不要繼續(xù)做處理,再繼續(xù)返回給上一層。
到這一步,基本就把攔截器的工作流程梳理清楚了,再回頭來(lái)看實(shí)現(xiàn)攔截器的方法里做的事情,就清晰很多了:

class InterceptorA implements Interceptor{
        @Override
        public Response intercept(Chain chain) throws IOException {
            //上一層構(gòu)造好的chain對(duì)象,傳入本層,里面包含了整個(gè)的攔截器列表和上一層傳入的request對(duì)象
            Request request = chain.request();//chain里面可以隨時(shí)取出request對(duì)象進(jìn)行加工處理
            //處理requeset...

            //porceed方法負(fù)責(zé)將本層加工處理好的request對(duì)象構(gòu)造出下一層的chain對(duì)象
            //并驅(qū)動(dòng)執(zhí)行下一層攔截器的intercept(Chain chain) 方法,獲取下一層返回的response
            Response response = chain.proceed(request);
            
            //獲取到下一層返回回來(lái)的response之后,根據(jù)自身需要來(lái)決定是否要處理response或者根據(jù)
            //response的信息決定要不要做一些額外的事情
            //處理response...

            return response;//返回response給上一層
        }
    }

這就是單個(gè)攔截器所做的所有事情。
然后,當(dāng)加入多個(gè)攔截器之后,處理的大致流程就變得如下圖所示:


多層攔截器

以上,攔截器整個(gè)的運(yùn)行機(jī)制基本就梳理完了。

...@Y(^ _ ^)Y@ ...

最后編輯于
?著作權(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ù)。

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