OKHTTP 緩存解析

參考:OkHttp3 Cache

拆輪子系列:拆 OkHttp

  • 首先理解下緩存的幾種cachecontrol ,這里網上很多,不多贅述

Cache-Control:

Cache-Control指定請求和響應遵循的緩存機制。在請求消息或響應消息中設置Cache-Control并不會修改另一個消息處理過程中的緩存處理過程。請求時的緩存指令有下幾種:

  • Public指示響應可被任何緩存區緩存。
  • Private指示對于單個用戶的整個或部分響應消息,不能被共享緩存處理。這允許服務器僅僅描述當用戶的部分響應消息,此響應消息對于其他用戶的請求無效。
  • no-cache指示請求或響應消息不能緩存
  • no-store用于防止重要的信息被無意的發布。在請求消息中發送將使得請求和響應消息都不使用緩存。
  • max-age指示客戶機可以接收生存期不大于指定時間(以秒為單位)的響應。
  • min-fresh指示客戶機可以接收響應時間小于當前時間加上指定時間的響應。
  • max-stale指示客戶機可以接收超出超時期間的響應消息。如果指定* * max-stale消息的值,那么客戶機可以接收超出超時期指定值之內的響應消息。

緩存在okhttp中的處理有兩種

一.官方推薦使用在request中添加CacheControl

  • CacheControl.FORCE_CACHE
  • CacheControl.FORCE_NETWORK
  • 3.使用maxStale
Request request = new Request.Builder()
                .cacheControl(new CacheControl.Builder()
                        .maxStale(10, TimeUnit.SECONDS)
                        .build())
                .url("http://publicobject.com/helloworld.txt")
                .build();
  • 4.maxAge
Request request = new Request.Builder()
               .cacheControl(new CacheControl.Builder()
                       .maxAge(10, TimeUnit.SECONDS)
                       .build())
               .url("http://publicobject.com/helloworld.txt")
               .build();

上邊試過3、4 是在請求頭中加入參數,然后在CacheInterceptor中進行了處理(借助CacheStrategy 類的private CacheStrategy getCandidate()方法).

/** Returns a strategy to use assuming the request can use the network. */
    private CacheStrategy getCandidate() {
      // No cached response.
      if (cacheResponse == null) {
        return new CacheStrategy(request, null);
      }

      // Drop the cached response if it's missing a required handshake.
      if (request.isHttps() && cacheResponse.handshake() == null) {
        return new CacheStrategy(request, null);
      }

      // If this response shouldn't have been stored, it should never be used
      // as a response source. This check should be redundant as long as the
      // persistence store is well-behaved and the rules are constant.
      if (!isCacheable(cacheResponse, request)) {
        return new CacheStrategy(request, null);
      }

      CacheControl requestCaching = request.cacheControl();
      if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }

      long ageMillis = cacheResponseAge();
      long freshMillis = computeFreshnessLifetime();

      if (requestCaching.maxAgeSeconds() != -1) {
        freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
      }

      long minFreshMillis = 0;
      if (requestCaching.minFreshSeconds() != -1) {
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
      }

      long maxStaleMillis = 0;
      CacheControl responseCaching = cacheResponse.cacheControl();
      if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
      }

      if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        Response.Builder builder = cacheResponse.newBuilder();
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
        }
        long oneDayMillis = 24 * 60 * 60 * 1000L;
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
        }
        return new CacheStrategy(null, builder.build());
      }

      // Find a condition to add to the request. If the condition is satisfied, the response body
      // will not be transmitted.
      String conditionName;
      String conditionValue;
      if (etag != null) {
        conditionName = "If-None-Match";
        conditionValue = etag;
      } else if (lastModified != null) {
        conditionName = "If-Modified-Since";
        conditionValue = lastModifiedString;
      } else if (servedDate != null) {
        conditionName = "If-Modified-Since";
        conditionValue = servedDateString;
      } else {
        return new CacheStrategy(request, null); // No condition! Make a regular request.
      }

      Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
      Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);

      Request conditionalRequest = request.newBuilder()
          .headers(conditionalRequestHeaders.build())
          .build();
      return new CacheStrategy(conditionalRequest, cacheResponse);
    }

二.使用攔截器,Interceptor

OKHTTP之責任鏈理解

因為責任鏈的順序是 cache -> your Application Interceptor -> network Interceptor 所以你的interceptor 要是緩存處理掉話需要 okhttpclient.addInterceptor(); 要是返回結果添加請求頭的話需要使用addNetworkInterceptor();

public class OkLibCacheInterceptor implements Interceptor {

    private Context context;

    public OkLibCacheInterceptor(Context context) {
        this.context = context;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();
        //判斷網絡狀態  直接取緩存,是在最上層進行攔截,但是我們的OkLibCacheInterceptor 是addNetworkInterceptor() ,在網絡請求之后進行的
        //所以這里不會被遞歸調用到,一旦網絡失敗便不會進入此攔截器了 ,學習下,所以這里的使用緩存是不管用的得需要在請求時加入,或者使用addInterceptor 放在網絡攔截器之前
        if (!isNetworkConnected(context)) {
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();
            Log.e("OkLibCacheInterceptor", "暫無網絡");
        }

        Log.e("新請求", "=request==" + request.toString());
        Response response = chain.proceed(request);

        if (isNetworkConnected(context)) {
            int maxAge = 60 * 60 * 24; // 有網絡的時候從緩存1天后失效
            response = response.newBuilder()
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, max-age=" + maxAge)
                    .build();
        } else {
            int maxStale = 60 * 60 * 24 * 28; // // 無網絡緩存保存四周
            response = response.newBuilder()
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .build();
        }
        Log.e("oklibCacheInter", response.headers().toMultimap().toString());
        return response;
    }

    public boolean isNetworkConnected(Context context) {
        if (context != null) {
            ConnectivityManager mConnectivityManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable();
            }
        }
        return false;
    }


}

NOTE

  • 1.默認okhttp緩存只支持get請求,不支持post請求,post需要自己進行緩存
  • 2.進行了抓包在使用maxAge的時候依然發起請求,當然我在使用攔截器的時候沒事.這里我看了下官網的介紹是
This technique works even better in situations where a stale response is better than no response. To permit stale cached responses, use the max-stale directive with the maximum staleness in seconds:

Request request = new Request.Builder()
    .cacheControl(new CacheControl.Builder()
        .maxStale(365, TimeUnit.DAYS)
        .build())
    .url("http://publicobject.com/helloworld.txt")
    .build();

所以使用maxStale 進行處理

//todo 這里maxAge maxStale 區別還是沒怎么搞懂,記錄下

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,981評論 19 139
  • Volley源碼分析之流程和緩存 前言 Android一開始提供了HttpURLConnection和HttpCl...
    大寫ls閱讀 645評論 0 6
  • 緩存分類 http請求有服務端和客戶端之分。因此緩存也可以分為兩個類型服務端側和客戶端側。 緩存——服務端常見的服...
    安仔夏天勤奮閱讀 610評論 1 0
  • 參考Android網絡請求心路歷程Android Http接地氣網絡請求(HttpURLConnection) 一...
    合肥黑閱讀 21,365評論 7 63
  • 開了家庭會議,確定姥爺.婆婆下廣東。 公司決議6月13-18日沙巴旅游,我預計我趕不上了 每天開啟學習模式:大學課...
    薇薇董閱讀 147評論 0 0