OKHTTP攔截器BridgeInterceptor的簡單分析

OKHTTP異步和同步請求簡單分析
OKHTTP攔截器緩存策略CacheInterceptor的簡單分析
OKHTTP攔截器ConnectInterceptor的簡單分析
OKHTTP攔截器CallServerInterceptor的簡單分析
OKHTTP攔截器BridgeInterceptor的簡單分析
OKHTTP攔截器RetryAndFollowUpInterceptor的簡單分析
OKHTTP結(jié)合官網(wǎng)示例分析兩種自定義攔截器的區(qū)別

BridgeInterceptor

/**
 * Bridges from application code to network code. First it builds a network request from a user
 * request. Then it proceeds to call the network. Finally it builds a user response from the network
 * response.
 */
public final class BridgeInterceptor implements Interceptor {
  private final CookieJar cookieJar;

  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

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

    RequestBody body = userRequest.body();
    //對請求頭的補充
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }
    //默認是保持連接的 Keep-Alive
    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    //默認是 GZIP 壓縮的
    //Accept-Encoding : 就是告訴服務(wù)器客戶端能夠接受的數(shù)據(jù)編碼類型植袍,OKHTTP 默認就是 GZIP 類型肛宋。
    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null) {
      //標(biāo)記請求支持 GZIP 壓縮
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }
    //cookie 頭的添加
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }
    //user-egent "okhttp/3.4.1"
    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }
    //發(fā)送網(wǎng)絡(luò)請求
    Response networkResponse = chain.proceed(requestBuilder.build());
    //接受服務(wù)器返回的 Cookie
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);

    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      //當(dāng)服務(wù)器返回的數(shù)據(jù)是 GZIP 壓縮的堰塌,那么客戶端就有責(zé)任去進行解壓操作
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }
    //構(gòu)建一個Response
    return responseBuilder.build();
  }

  /** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
  private String cookieHeader(List<Cookie> cookies) {
    StringBuilder cookieHeader = new StringBuilder();
    for (int i = 0, size = cookies.size(); i < size; i++) {
      if (i > 0) {
        cookieHeader.append("; ");
      }
      Cookie cookie = cookies.get(i);
      cookieHeader.append(cookie.name()).append('=').append(cookie.value());
    }
    return cookieHeader.toString();
  }
}

BridgeInterceptor 攔截器的功能主要有以下 3 點:

  • 是負責(zé)將用戶構(gòu)建的一個 Request 請求轉(zhuǎn)化為能夠進行網(wǎng)絡(luò)訪問的請求。

  • 將這個符合網(wǎng)絡(luò)請求的 Request 進行網(wǎng)絡(luò)請求。

  • 將網(wǎng)絡(luò)請求回來的響應(yīng) Response 轉(zhuǎn)化為用戶可用的 Response态罪。

下面會通過源碼的角度來分析這三點是如何實現(xiàn)的?

1炉峰、用戶 Request 到 網(wǎng)絡(luò) Request 的轉(zhuǎn)換

因為用戶在構(gòu)建一個 Request 對象的時侯往往只會簡單設(shè)置 url,RequestBody 等幾個屬性,其實這樣就已經(jīng)可以發(fā)送一個網(wǎng)絡(luò)請求了疼阔,但是真正要發(fā)送一個請求實際上沒這么簡單,在 OKHTTP 這么優(yōu)秀的框架的光環(huán)下婆廊,使調(diào)用者使用這個框架去發(fā)送一個網(wǎng)絡(luò)請求是非常簡單的迅细,因為很多功能它內(nèi)部都進行了高度的封裝。

如何對用戶 Request 到 網(wǎng)絡(luò) Request 的轉(zhuǎn)換呢茵典?從上面的代碼中可以看出它在原來 request
的基礎(chǔ)上添加了很多請求頭,下面列舉的是添加的頭信息统阿。

  • Content-Type 定義網(wǎng)絡(luò)文件的類型和網(wǎng)頁的編碼枚尼,如果未指定 ContentType,默認為[TEXT]/[HTML]砂吞,具體的類型署恍,參考這個文檔 HTTP Content-type

  • Content-Length 表示的是請求體內(nèi)容的長度。它和 Transfer-Encoding 是互斥的盯质,主要根據(jù) Content-Length 是否為 -1 進行判讀使用哪一個請求頭。

  • Transfer-Encoding 值為 chunked 表示請求體的內(nèi)容大小是未知的呼巷。

  • Host 請求的 url 的主機

  • Connection 默認就是 "Keep-Alive"赎瑰,就是一個 TCP 連接之后不會關(guān)閉王悍,保持連接狀態(tài)餐曼。

  • Accept-Encoding 默認是 "gzip" 告訴服務(wù)器客戶端支持 gzip 編碼的響應(yīng)。

  • Cookie 當(dāng)請求設(shè)置了 Cookie 那么就是添加 Cookie 這個請求頭源譬。

  • User-Agent "okhttp/3.4.1" 這個值根據(jù) OKHTTP 的版本不一樣而不一樣,它表示客戶端 的信息踩娘。

上面就是將一個普通的 Request 添加很多頭信息,讓其成為可以發(fā)送網(wǎng)絡(luò)請求的 Request 雷绢。

2理卑、將這個 Request 進行網(wǎng)絡(luò)請求得到響應(yīng) Response

這個過程涉及到其他幾個攔截器的操作翘紊,具體是怎么進行網(wǎng)絡(luò)請求傻工,我們暫時不要管,具體查閱我先前寫幾篇 BLOG 中捆。我們只管這一步操作是一個向服務(wù)發(fā)送請求的操作坊饶,并且這個請求之后服務(wù)端會給客戶端返回一個 Response 響應(yīng)就行了。

Response networkResponse = chain.proceed(requestBuilder.build());

3匿级、將網(wǎng)絡(luò)請求回來的響應(yīng) Response 轉(zhuǎn)化為用戶可用的 Response染厅。

這句話怎么理解呢津函?例如 OKHTTP 默認就是支持 GZIP 壓縮的肖粮,若服務(wù)器返回的響應(yīng)體是經(jīng)過 GZIP 壓縮的,那么 BridgeInterceptor 就有責(zé)任將其進行解壓尔苦,那么調(diào)用者無需關(guān)系這個過程了涩馆,只要得到最終的 Response 即可允坚。

3.1、獲取服務(wù)器返回的 Cookie

HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

3.2稠项、將網(wǎng)絡(luò)請求返回的響應(yīng)進行轉(zhuǎn)化

當(dāng) transparentGzip 為 true ,表示請求設(shè)置的 Accept-Encoding 是 支持gzip 壓縮的展运,意思就是告知服務(wù)器客戶端是支持 gzip 壓縮的,然后再判斷服務(wù)器的響應(yīng)頭 Content-Encoding 是否也是 GZIP 壓縮的划乖,意思就是響應(yīng)體內(nèi)容是否是經(jīng)過 GZIP 壓縮的挤土,如果都成立的條件下,那么它會將 Resposonse.body().source() 的輸入流 BufferedSource 轉(zhuǎn)化為 GzipSource 類型仰美,這樣的目的就是讓調(diào)用者在使用 Response.body().string() 獲取響應(yīng)內(nèi)容時就是以解壓的方式進行讀取流數(shù)據(jù)。

if (transparentGzip
    && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
    && HttpHeaders.hasBody(networkResponse)) {
  GzipSource responseBody = new GzipSource(networkResponse.body().source());
  Headers strippedHeaders = networkResponse.headers().newBuilder()
      .removeAll("Content-Encoding")
      .removeAll("Content-Length")
      .build();
  responseBuilder.headers(strippedHeaders);
  responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}

4庆寺、總結(jié)

BidgeInterceptor 這個攔截器的功能相對簡單一些诉字,主要就是上面提及到 3 點內(nèi)容懦尝。這里設(shè)計的一個 Cookie 的操作壤圃,本文并沒有詳細分析實現(xiàn),以后再看看吧踊挠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末冲杀,一起剝皮案震驚了整個濱河市睹酌,隨后出現(xiàn)的幾起案子剩檀,更是在濱河造成了極大的恐慌,老刑警劉巖沪猴,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異则披,居然都是意外死亡洗出,警方通過查閱死者的電腦和手機士复,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門阱洪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菠镇,“玉大人冗荸,你說我怎么就攤上這事利耍。” “怎么了程癌?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵轴猎,是天一觀的道長。 經(jīng)常有香客問我捻脖,道長,這世上最難降的妖魔是什么可婶? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任扰肌,我火速辦了婚禮,結(jié)果婚禮上曙旭,老公的妹妹穿的比我還像新娘。我一直安慰自己桂躏,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布蛮位。 她就那樣靜靜地躺著鳞绕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪们何。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天拂封,我揣著相機與錄音鹦蠕,去河邊找鬼冒签。 笑死钟病,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肠阱。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼惰说,長吁一口氣:“原來是場噩夢啊……” “哼缘回!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酥宴,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤拙寡,失蹤者是張志新(化名)和其女友劉穎授滓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體在孝,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡淮摔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了和橙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡晰搀,死狀恐怖办斑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情俄周,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布建丧,位于F島的核電站波势,受9級特大地震影響翎朱,放射性物質(zhì)發(fā)生泄漏尺铣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一澈灼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叁熔,春花似錦床牧、人聲如沸荣回。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽删铃。三九已至,卻和暖如春泳姐,著一層夾襖步出監(jiān)牢的瞬間暂吉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工阎肝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人风题。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓嫉父,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绕辖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容