Android小知識-剖析OkHttp中的五個攔截器(上篇)

本平臺的文章更新會有延遲,你們能夠關注微信公衆號-顧林海,包括年末前會更新kotlin由淺入深系列教程,目前計劃在微信公衆號進行首發,若是你們想獲取最新教程,請關注微信公衆號,謝謝java

前面幾節介紹了OkHttp的同步和異步請求的總體流程以及Dispatcher分發器的做用,接下來介紹一下OkHttp中的五個攔截器。web

RetryAndFollowUpInterceptor攔截器

RetryAndFollowupInterceptor是重試重定向攔截器,它的主要做用是負責失敗重連。OkHttp中的重定向功能是默認開啓的。服務器

該攔截器方法以下:微信

@Override public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        ...
        //標記1
        StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
                createAddress(request.url()), call, eventListener, callStackTrace);
        ...
        while (true) {
            //取消,釋放
            if (canceled) {
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response;
            boolean releaseConnection = true;
            ...
            //標記2
            response = realChain.proceed(request, streamAllocation, null, null);
            releaseConnection = false;
            ...
            Request followUp;
            //標記3
            followUp = followUpRequest(response, streamAllocation.route());
            ...
            //標記4
            if (++followUpCount > MAX_FOLLOW_UPS) {
                streamAllocation.release();
                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
            }
            ...
        }
    }
複製代碼

在intercept方法中省略了一些代碼,咱們只看核心代碼:cookie

在標記1處建立了StreamAllocation對象,建立的StreamAllocation對象在RetryAndFollowupInterceptor攔截器中並無使用到。網絡

StreamAllocation對象會經過標記2處的proceed方法傳遞給下一個攔截器,直到ConnectInterceptor攔截器,StreamAllocation的主要做用是提供創建HTTP請求所須要的網絡組件,ConnectInterceptor從RealInterceptorChain獲取前面的Interceptor傳過來的StreamAllocation對象,執行StreamAllocation對象的newStream()方法完成全部的鏈接創建工做,並將這個過程當中建立的用於網絡IO的RealConnection對象,以及與服務器交互最爲關鍵的HttpCodec等對象傳遞給後面的CallServerInterceptor攔截器。異步

在標記3處,會對返回的Response進行檢查,經過followUpRequest方法對Response返回的狀態碼判斷,並建立重定向須要發出的Request對象。ide

followUpRequest代碼以下:ui

private Request followUpRequest(Response userResponse, Route route) throws IOException {
        if (userResponse == null) throw new IllegalStateException();
        int responseCode = userResponse.code();
        final String method = userResponse.request().method();
        switch (responseCode) {
            case HTTP_PROXY_AUTH://407 代理服務器認證
                ...
            case HTTP_UNAUTHORIZED://401 身份未認證
                ...
            case HTTP_PERM_REDIRECT://308
            case HTTP_TEMP_REDIRECT://307
                //當請求的method不爲GET和HEAD時不進行重定向
                ...
            case HTTP_MULT_CHOICE://300 多種選擇
            case HTTP_MOVED_PERM://301 永久移除
            case HTTP_MOVED_TEMP://302 臨時重定向
            case HTTP_SEE_OTHER://303 其餘問題
                ...
            case HTTP_CLIENT_TIMEOUT://408 請求超時
                ...
            case HTTP_UNAVAILABLE://503 服務不可用
                ...
            default:
                return null;
        }
    }
複製代碼

followUpRequest方法主要是對返回的狀態碼進行判斷,根據特定的狀態碼建立重定向須要的Request對象。編碼

回到上面攔截器intercept方法,在標記4判斷followUpCount是否大於MAX_FOLLOW_UPS(20),也就是說重定向次數上限爲20,當重試次數超過20的時候,會釋放StreamAllocation這個對象,這樣作是爲了防止無限制的重試網絡請求,從而形成資源的浪費,關於重定向的次數建議能夠按照Chrome遵循21個重定向;Firefox、CURL和WGET遵循20;Safari遵循16;HTTP/1推薦5。

總結RetryAndFollowupInterceptor攔截器:

  1. 建立StreamAllocation對象。

  2. 調用RealInterceptorChain.proceed()進行網絡請求。

  3. 根據異常結果或響應結果判斷是否進行從新請求。

  4. 調用下一個攔截器,對Response進行處理,返回給上一個攔截器。

BridgeInterceptor攔截器

BridgeInterceptor是橋接和適配攔截器,它的做用是設置內容長度、編碼方式以及壓縮等等一系列操做,主要是添加頭部的做用。

該攔截器方法以下:

@Override public Response intercept(Interceptor.Chain chain) throws IOException {
        Request userRequest = chain.request();
        Request.Builder requestBuilder = userRequest.newBuilder();

        //====================添加頭部信息========================
        RequestBody body = userRequest.body();
        ...
        if (userRequest.header("Connection") == null) {
            //標記1
            requestBuilder.header("Connection", "Keep-Alive");
        }
        ...
        //標記2
        Response networkResponse = chain.proceed(requestBuilder.build());
        //標記3
        HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

        Response.Builder responseBuilder = networkResponse.newBuilder()
                .request(userRequest);
        //標記4
        if (transparentGzip
                && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
                && HttpHeaders.hasBody(networkResponse)) {
            //標記5
            GzipSource responseBody = new GzipSource(networkResponse.body().source());
           ...
        }

        return responseBuilder.build();
    }
複製代碼

intercept方法主要是給Request添加一些頭部信息,在標記1處的Connection設置爲Keep-Alive,Keep-Alive的做用是當咱們開啓一個HTTP鏈接後,在必定時間內保持它的鏈接狀態。

在標記2處調用了攔截器鏈的proceed方法向服務器發起請求。

在標記3處經過HttpHeaders的receiveHeaders方法,經過網絡請求,服務器返回的Response轉化爲客戶端能夠識別的Response,若是HTTP默認支持gzip,那麼BridgeInterceptor攔截器將會對這個Response進行解壓,最終獲得客戶端使用的Response。

在標記4處,對transparentGzip標誌位進行判斷,若是transparentGzip爲true就表面Accept-Encoding支持gzip壓縮;再判斷頭部的Content-Encoding是否爲gzip,保證服務端返回的響應體內容是通過了gzip壓縮的;最後判斷HTTP頭部是否有Body。當知足這些條件後在標記5處將Response的body轉換爲GzipSource類型,這樣的話client端直接以解壓的方式讀取數據。

總結BridgeInterceptor攔截器:

  1. 負責將用戶構建的一個Request請求轉化爲可以進行網絡訪問的請求,經過給頭部添加信息。

  2. 將這個符合網絡請求的Request進行網絡請求。

  3. 將網絡請求回來的響應Response轉化爲用戶可用的Response。


838794-506ddad529df4cd4.webp.jpg

搜索微信「顧林海」公衆號,按期推送優質文章。

相關文章
相關標籤/搜索