1. 程式人生 > 程式設計 >OkHttp(三) - 攔截器鏈處理過程分析

OkHttp(三) - 攔截器鏈處理過程分析

前面分析了請求的具體執行流程,請求最終會經過一個由多個攔截器組成的鏈條來處理具體的請求和響應,這個便是我們熟知的呼叫鏈(責任鏈)模式

責任鏈模式


責任鏈模式(Chain of Responsibility Pattern)為請求建立了一個接收者物件的鏈。為了避免請求傳送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一物件記住其下一個物件的引用而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有物件處理它為止。如圖所示:

上圖形象地描述了責任鏈模式的工作狀態,這裡就不具體展開介紹了,如需進一步瞭解,可參考相關資料。

攔截器鏈


在OkHttp中,責任鏈中的處理器是各種攔截器,除了自身提供的5個核心攔截器外,它還允許使用者自定義攔截器,並加入其攔截器鏈鏈中。先來看看這條“鏈”的介面定義:

  interface Chain {
    //返回請求物件
    Request request();
    //執行具體的請求,並獲取響應
    Response proceed(Request request) throws IOException;
    //返回連線
    Connection connection();
  }
複製程式碼

其中proceed的方法很重要,它定義輸入請求,輸出響應,具體的實現子類來執行。RealInterceptorChain是其實現的子類,其proceed的方法實現如下:

    @Override
    public Response proceed(Request request) throws IOException {
        return
proceed(request,streamAllocation,httpCodec,connection); } 複製程式碼

攔截器鏈的執行方式

RealInterceptorChain實現的proceed方法呼叫了自己的proceed的方法,這個方法是攔截器鏈模式的實現,它定義了我們我們的請求是如何經過層層攔截器處理,獲取響應的過程,實現細節參考下面的程式碼:

    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,connection,index + 1,request); 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"); } return response; } 複製程式碼

從上面的程式碼中提煉了三句最最核心的程式碼:

// Call the next interceptor in the chain.
//每次呼叫取出下一個攔截器,index = index+1就表示下一個攔截器的索引
        RealInterceptorChain next = new RealInterceptorChain(
                interceptors,request);
//取出要處理的攔截器
        Interceptor interceptor = interceptors.get(index);
 //呼叫每個攔截器的intercept方法,每個攔截器中都要呼叫chain的proceed方法,這樣就是相當於把請求傳遞到下一個攔截器處理
        Response response = interceptor.intercept(next);
複製程式碼

這三句程式碼實現了請求在不同攔截器間的處理和向下傳遞。上面程式碼中有幾個關鍵的引數: interceptors:攔截器集合,所有的攔截器都儲存於此集合中; index:攔截器的索引,用於表示當前處理到的攔截器在interceptors列表中的索引位置; 在實現自定義攔截器的時候,一定不要忘了呼叫的chain的proceed方法,否則鏈條就要斷裂。

“鏈條”的建立

瞭解了okhttp中攔截器鏈的執行方式,來看看這個鏈條是怎麼建立的,以及框架預設建立了哪些攔截器。回到RealCall物件中,請求的發起是通過getResponseWithInterceptorChain實現的,參考程式碼如下:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    //新增使用者建立的攔截器到攔截器鏈中,並先於系統預設的攔截器之前執行
    interceptors.addAll(client.interceptors());
    //用於錯誤恢復和重定向跟蹤,建立了後面要使用的StreamAllocation類
    interceptors.add(retryAndFollowUpInterceptor);
    //此類用於預處理請求和響應,如預設新增許多請求頭和解析響應頭
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //根據需要是否從快取中返回響應
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //建立請求端連線
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    //完成請求體和請求體資料的傳輸,並獲取響應資料
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //建立攔截器鏈
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors,null,originalRequest);
    //通過攔截器執行具體的請求
    return chain.proceed(originalRequest);
  }
複製程式碼

此方法中建立了預設的幾個核心攔截器,並加入到interceptors集合中,接著建立Chain,並呼叫了proceed方法,由此開始了請求的處理。

總結


前面分析了okhttp中的攔截器鏈的建立和執行方式,整個框架的核心部分都在於此。它支撐著這個框架的執行和擴充套件,大家可細細品味!掌握了這個框架執行的思路,下面我們將會深入分析框架中的幾個預設核心攔截器。