天天看點

OkHttp3.0(四)-Interceptor攔截器(1)-攔截器鍊Chain1.引言2.getResponseWithInterceptorChain()方法3.Interceptor.Chain,攔截器鍊4.總結

1.引言

我們在前面幾個章節的學習過程中,已經可以明白OkHttp的基本使用,OkHttp在使用的過程中幾個常用的類的分析,OkHttp的配置設定器Dispatcher。其中在RealCall中,不管是同步請求還是異步請求,最終都會調用的一個方法,得到網絡請求響應結果Response,就是getResponseWithInterceptorChain()方法。因為這個方法用的是攔截器鍊調用攔截器執行對應的方法,是以我們決定放到現在來講,我們先看一下getResponseWithInterceptorChain()的調用位置:

//同步請求擷取請求結果
 @Override public Response execute() throws IOException {
    ...
    try {
      ...
      Response result = getResponseWithInterceptorChain();//擷取請求結果
      ...
      return result;
    } catch (IOException e) {
      ...
    } finally {
      ...
    }
  }
           

上面我們看到的,是我們調用Call的同步請求方法execute()時,的部分代碼,可以看到,在該方法中真正擷取請求結果的隻有一句代碼Response result = getResponseWithInterceptorChain(),就是說,同步請求會直接調用getResponseWithInterceptorChain()方法,擷取Response并傳回個調用者。

我們再看異步請求是如何調用的getResponseWithInterceptorChain()方法,首先我們異步請求調用的是Call的enqueue(Callback)方法:

//Call的異步請求方法,請求結果會利用Callback接口回調傳回給調用者
 @Override public void enqueue(Callback responseCallback) {
    ...
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
           

通過部分代碼,我們看到,Call的enqueue(Callback)方法,會調用Dispatcher的enqueue(AsyncCall)方法,我們繼續看一下Dispatcher的enqueue(AsyncCall)方法:

//Dispatcher的異步請求方法,被Call調用。Asyncall是異步請求任務
 synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);//線程池執行異步任務,最終會執行AsyncCall的execute()方法
    } else {
      readyAsyncCalls.add(call);
    }
  }
           

上面的代碼我們前面已經講過多次,executorService().execute(call)使用線程池異步執行了異步請求任務,其中的請求任務代碼,在AsyncCall類的execute()方法中,相當于在異步線程中執行了AsyncCall類的execute()方法,是以我們在跳轉到AsyncCall類中,檢視一下它的execute()方法:

//RealCall的内部類AsyncCall,用來做異步請求任務,是Runnable的子類。  
 @Override protected void execute() {
      ...
      try {
        //擷取請求結果
        Response response = getResponseWithInterceptorChain();
        if (...) {
          ...//判斷,使用Callback接口回調傳回給調用者
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
         ...//判斷,使用Callback接口回調傳回給調用者
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
         ...
        } else {
          ...//判斷,使用Callback接口回調傳回給調用者
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        ...
      }
    }
           

上面代碼,相信大家都能看懂,在AsyncCall的execute()方法中調用了getResponseWithInterceptorChain()方法。擷取到了請求結果,然後經過判斷,在利用調用者傳進來的Callback接口,做接口回調,将結果回傳給調用者,完成了異步請求。

2.getResponseWithInterceptorChain()方法

前面說了這麼多,就是為了說明不管是同步請求還是異步請求,都會調用RealCall中的getResponseWithInterceptorChain()來擷取請求結果Response,是以我們現在将getResponseWithInterceptorChain()代碼放上來,分析一下:

//RealCall的同步、異步請求都會調用此方法,這是擷取網絡請求結果Response的關鍵方法 
Response getResponseWithInterceptorChain() throws IOException {
    //建立一個存儲攔截器的集合
    List<Interceptor> interceptors = new ArrayList<>();
    //将使用者傳入的Application攔截器添加到集合中
    interceptors.addAll(client.interceptors());
    //将重定向攔截器添加到集合中
    interceptors.add(retryAndFollowUpInterceptor);
    //将橋接攔截器加入到集合中
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //将緩存攔截器加入到集合中
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //将連接配接攔截器加入到集合中
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {//如果不是WebSocket連接配接
      //将使用者傳入的Network攔截器加入的
      interceptors.addAll(client.networkInterceptors());
    }
    //将CallServer攔截器(與伺服器進行資料互動)加入到集合中
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //建立攔截器鍊,index初始值為0
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    //調用攔截器鍊的proceed方法,就會調用到所有攔截器,并傳回請求結果Response
    return chain.proceed(originalRequest);
  }
           

這一節,我們先不講代碼中提到的每個攔截器的作用,我們隻講攔截器的調用。我們從上面代碼中可以看到,首先會将各種攔截器按照先後順序,分别加入到新建立的ArryList集合中,然後建立了Interceptor.Chain攔截器鍊執行個體,在建立過程中,構造函數傳入了存有各種攔截器的集合interceptors、數字0、使用者建立好的Request對象、目前RealCall對象、OkHttp網絡請求詳細事件回調接口、逾時設定。最後,調用了攔截器鍊chain的proceed(Request)方法,該方法傳回Response正是調用者需要的伺服器端的響應結果。我們先大概知道一下,這個方法中,主要做了些什麼工作。接下來我們來看攔截器鍊。

3.Interceptor.Chain,攔截器鍊

首先我們通過代碼查找,看到Interceptor.Chain是一個interface,它的目前唯一實作類就是RealInterceptorChain,我們看下這個類的構造方法

//RealInterceptorChain構造方法
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;//RealCall那邊建立好并且已經添加好了所有攔截器
    this.connection = connection;//null
    this.streamAllocation = streamAllocation;//null
    this.httpCodec = httpCodec;//null
    this.index = index;//RealCall傳入的是0
    this.request = request;//使用者建構好的Request
    this.call = call;//目前RealCall
    this.eventListener = eventListener;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
  }
           

我們需要注意一點,他的變量index正是我們剛才RealCall的getResponseWithInterceptorChain()方法中傳入的0,我們接着看一下我們前面調用的RealInterceptorChain的proceed(Request)方法:

//首次調用攔截器鍊
 @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }
  //繼續調用攔截器鍊,将擷取到的Response傳回去
  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    ...
    //這裡需要注意,建立的攔截器鍊RealInterceptorChain,傳入的參數中(index+1)
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    //這裡需要注意,擷取到的攔截器是角标為index的攔截器,而index正是通過其構造函數指派的
    Interceptor interceptor = interceptors.get(index);
    //調用攔截器的intercept()方法擷取Response
    Response response = interceptor.intercept(next);
    ...
    return response;//将請求結果傳回
  }
           

講到這裡,可能很多人還是有些懵懵懂懂,我來幫大家捋一下:

1、在RealCall的getResponseWithInterceptorChain()方法中,我們第一次建立了攔截器鍊對象RealInterceptorChain,将存有攔截器的集合interceptors傳給了RealInterceptorChain,同時設定了它的index為0,緊接着調用了RealInterceptorChain的proceed方法;

2、在RealInterceptorChain的proceed方法中,再次建立了RealInterceptorChain的執行個體,繼續将存有攔截器的集合interceptors傳遞給新建立的RealInterceptorChain,這時給他設定的index為目前index+1,然後從interceptors中擷取角标為目前index的Interceptor,執行Interceptor的intercept(Chain)方法,參數Chain就是新建立的index+1的RealInterceptorChain對象,并将擷取到的Response傳回給上一個調用者;

3、Interceptor本身是interface,其intercept(Chain)方法具體看它的實作類,可以肯定的一點是,intercept(Chain)方法中,除了做攔截器本身該做的一些操作外,會繼續調用Chain的proceed方法,進入Chain的proceed方法中繼續建立index+1的Chain,并且擷取角标為index的Interceptor,調用它的intercept(Chain)方法......一直調用下去,直到最後一個攔截器為止。

上面我說捋一下,我想還有人不太明白,因為到現在為止,還缺少一部分代碼,那就是Interceptor的intercept(Chain)方法究竟是怎麼樣子的,我們先看下Interceptor接口的類繼承情況

OkHttp3.0(四)-Interceptor攔截器(1)-攔截器鍊Chain1.引言2.getResponseWithInterceptorChain()方法3.Interceptor.Chain,攔截器鍊4.總結

其中LoggingInterceptor是我自己建立的繼承自Interceptor,其他五個都是OkHttp架構已經提供的攔截器,我們在剛才講getResponseWithInterceptorChain()方法時候,都見過的,先不糾結每一個攔截器的作用。我們主要是為了研究一下這些攔截器的intercept(Chain)方法。getResponseWithInterceptorChain()方法建立了存儲攔截器的ArrayList集合,并且将攔截器都存入了該集合,我們剛才提到,會根據順序,每次index+1來擷取interceptors中的Interceptor,并調用其intercept(Chain)方法。我們取兩個具有代表性的進行研究,這五個攔截器中,按照添加順序排在最前面的RetryAndFollowUpInterceptor和排在最後的CallServerInterceptor,分别看一下他們的intercept(Chain)方法

RetryAndFollowUpInterceptor的intercept(Chain)方法

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    ...
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    ...
    while (true) {
     ...//此處調用了攔截器鍊RealInterceptorChain的proceed方法
        response = realChain.proceed(request, streamAllocation, null, null);
     ...
        return response;
       ...
    }
  }
           

我們可以發現,在RetryAndFollowUpInterceptor的intercept(Chain)方法中,的确調用了攔截器鍊RealInterceptorChain的proceed()方法,這樣就可以繼續往下,調用下一個Interceptor的intercept(Chain)方法,一直調用下去,直到最後一個攔截器CallServerInterceptor結束。

CallServerInterceptor的intercept(Chain)

//這是CallServerInterceptor的intercept(Chain)方法,該方法不會調用Chain的proceed方法 
@Override public Response intercept(Chain chain) throws IOException {
    ...
    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
    ...
    return response;
  }
           

通過仔細閱讀該代碼,發現CallServerInterceptor果然沒有繼續調用Chain的proceed方法。攔截器鍊Chain的proceed方法,第一次調用,是被RealCall類的getResponseWithInterceptorChain()方法,然後proceed擷取角标為index的Interceptor,調用Interceptor的intercept(Chain)方法,index+1,intercept(Chain)方法繼續調用下一個RealInterceptorChain的proceed方法,一直這樣調用下去,直到最後一個攔截器,這就是攔截器鍊的調用。

4.總結

我們先通過兩幅幅圖來描述攔截器鍊的工作

OkHttp3.0(四)-Interceptor攔截器(1)-攔截器鍊Chain1.引言2.getResponseWithInterceptorChain()方法3.Interceptor.Chain,攔截器鍊4.總結
OkHttp3.0(四)-Interceptor攔截器(1)-攔截器鍊Chain1.引言2.getResponseWithInterceptorChain()方法3.Interceptor.Chain,攔截器鍊4.總結

我們來簡單的總結一下本章要跟大家分享的:

1、OkHttp的網絡請求,其本質上是由攔截器鍊的調用完成的;

2、攔截器鍊的調用,起始于RealCall的getResponseWithInterceptorChain方法,結束與最後一個攔截器CallServerInterceptor的intercept(Chain)方法;

3、連接配接器鍊的調用,RealInterceptorChain的作用是按照順序依次調用每一個攔截器的intercept(Chain)方法。攔截器Interceptor的作用是發揮自身攔截器的特性,對Request和Response做出操作,調用RealInterceptorChain的proceed()方法,讓其繼續下去;

4、我們可以将攔截器的調用看作遞歸,逐層向下調用,直到最後一個攔截器傳回Response,然後在逐層向上,一邊處理Response一邊傳回給上一層,最終傳回給調用。

本章節關于攔截器鍊的講解到此結束了,攔截器是OkHttp架構的精髓,是以我們會拿出幾篇文章來詳細分析,下一章節開始我們将分析每一個攔截器。

繼續閱讀