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 區別還是沒怎么搞懂,記錄下

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容

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