OKhttp源碼學習(六)—— 攔截器_CacheInterceptor

CacheInterceptor緩存攔截器分析

源碼地址:https://github.com/square/okhttp

不知不覺來到了第三個攔截器,經過前面的兩個攔截器:
RetryAndFollowUpInterceptor(初始化連接,重連);
BridgeInterceptor(頭處理,Gzip, cookie處理)。

而這個 CacheInterceptor,是處理緩存相關的攔截器。

緩存知識

對於http 緩存的相關知識,可以參考:
http://www.cnblogs.com/chenqf/p/6386163.html
這文章中比較詳細的講解了,http中的緩存機制,以及一些基本的原理。如果你對緩存已經瞭解,可以忽略。

緩存攔截器基本流程

  1. 讀取候選緩存;
  2. 創建緩存策略(根據頭信息,判斷強制緩存,對比緩存等策略);
  3. 根據策略,不使用網絡,緩存又沒有直接報錯;
  4. 根據策略,不使用網絡,有緩存就直接返回;
  5. 前面個都沒有返回,讀取網絡結果(跑下一個攔截器);
  6. 接收到的網絡結果,如果是code 304, 使用緩存,返回緩存結果(對比緩存)
  7. 讀取網絡結果;
  8. 對數據進行緩存;
  9. 返回網絡讀取的結果。

源碼解讀

  @Override public Response intercept(Chain chain) throws IOException {
   //1. 讀取候選緩存;
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();

    //2. 創建緩存策略(強制緩存,對比緩存等策略);
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    if (cache != null) {
      cache.trackResponse(strategy);
    }

    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }
    //根據策略,不使用網絡,緩存又沒有直接報錯;
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // 4. 根據策略,不使用網絡,有緩存就直接返回;
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
     // 5. 前面個都沒有返回,讀取網絡結果(跑下一個攔截器);
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

   //6. 接收到的網絡結果,如果是code 304, 使用緩存,返回緩存結果(對比緩存)
    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

   //7. 讀取網絡結果;
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();
    //8. 對數據進行緩存;
    if (HttpHeaders.hasBody(response)) {
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }
    //9. 返回網絡讀取的結果。
    return response;
  }

總結

緩存實際上是一個比較複雜的邏輯,這裏只是簡單的對攔截器的功能進行了分析,並沒有深入的分析其中的實現,但實際上緩存不屬於okhttp上的功能,只是對http協議做了處理,整體其實是屬於http相關的,而在okhttp中使用了攔截器的方式,進行了實現。

有興趣的可以繼續研究一下以下的點:

  1. 緩存策略是如何生成的,做了些什麼判斷
  2. 緩存是如何保存的

系列(簡書地址):
OKhttp源碼學習(一)—— 基本請求流程
OKhttp源碼學習(二)—— OkHttpClient
OKhttp源碼學習(三)—— Request, RealCall
OKhttp源碼學習(四)—— RetryAndFollowUpInterceptor攔截器
OKhttp源碼學習(五)—— BridgeInterceptor攔截器
OKhttp源碼學習(七)—— ConnectInterceptor攔截器
OKhttp源碼學習(八)——CallServerInterceptor攔截器
OKhttp源碼學習(九)—— 任務管理(Dispatcher)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章