使用Retrofit和Okhttp實現網絡緩存。無網讀緩存,有網根據過時時間從新請求 (轉)

使用Retrofit和Okhttp實現網絡緩存,更新於2016.02.02
原文連接:http://www.jianshu.com/p/9c3b4ea108a7


本文使用 Retrofit2.0.0-beta二、Okhttp 2.6.0(Okhttp3.0以後api寫法有變化)css

  • 配置Okhttp的Cache
  • 配置請求頭中的cache-control或者統一處理全部請求的請求頭
  • 雲端配合設置響應頭或者本身寫攔截器修改響應頭中cache-control

最後實現的效果是:有網的時候根據你每一個接口設置的須要緩存的時間(1分鐘、5分鐘等)進行緩存,過了時間從新請求;沒網的時候讀緩存。html

在這裏插一句爲何要作緩存,或者說有什麼好處?
減小服務器負荷,下降延遲提高用戶體驗。複雜的緩存策略會根據用戶當前的網絡狀況採起不一樣的緩存策略,好比在2g網絡不好的狀況下,提升緩存使用的時間;不用的應用、業務需求、接口所須要的緩存策略也會不同,有的要保證數據的實時性,因此不能有緩存,有的你能夠緩存5分鐘,等等。你要根據具體狀況所需數據的時效性狀況給出不一樣的方案。固然你也能夠所有都同樣的緩存策略,看你本身。git

1.配置okhttp中的Cache

OkHttpClient okHttpClient = new OkHttpClient();
File cacheFile = new File(context.getCacheDir(), "[緩存目錄]");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
okHttpClient.setCache(cache);

2.配置請求頭中的cache-control

緩存的相關知識和參數的說明,我是個連接 github

在Retrofit中,咱們能夠經過@Headers來配置,如:sql

@Headers("Cache-Control: public, max-age=3600) @GET("merchants/{shopId}/icon") Observable<ShopIconEntity> getShopIcon(@Path("shopId") long shopId);

沒有設置的能夠即爲有網的時候不進行緩存。api

或者你全部接口在有網的時候都不須要緩存或者都須要緩存且時間同樣,那麼也不用配置每一個接口的@Headers的Cache-Control了。緩存

3.雲端配合設置響應頭或者本身寫攔截器修改響應頭response中cache-control

到這一步緩存就已經待在你的緩存目錄了。
若是雲端有處裏cache的話,就已經能夠了。
可是極可能雲端沒有處理,因此返回的響應頭中cache-control是no-cache,這時候你仍是沒法作緩存,你們能夠用okhttp的寫日誌攔截器查看響應頭的內容。服務器

Okhttp Interceptors 使用說明,我是個連接網絡

若是雲端如今不方便處理的話,你也能夠本身搞定緩存的,那就是寫攔截器修改響應頭中的cache-control。我把請求頭中的cache-control讀出來而後設置到了響應頭中。測試

設置攔截器:
REWRITE_CACHE_CONTROL_INTERCEPTOR攔截器須要同時設置networkInterceptors和interceptors(OKHTTP3.0配置是否有效待我測試)

okHttpClient.interceptors().add(LoggingInterceptor);
okHttpClient.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR);
okHttpClient.interceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR);

攔截器以下:雲端響應頭攔截器,用來配置緩存策略

/** * 雲端響應頭攔截器,用來配置緩存策略 * Dangerous interceptor that rewrites the server's cache-control header. */private final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = chain -> {
    Request request = chain.request();
    if(!NetUtils.hasNetwork(context)){
        request = request.newBuilder()
                .cacheControl(CacheControl.FORCE_CACHE)
                .build();
        Logger.t(TAG).w("no network");
    }
    Response originalResponse = chain.proceed(request);
    if(NetUtils.hasNetwork(context)){
        //有網的時候讀接口上的@Headers裏的配置,你能夠在這裏進行統一的設置String cacheControl = request.cacheControl().toString();
        return originalResponse.newBuilder()
                .header("Cache-Control", cacheControl)
                .removeHeader("Pragma")
                .build();
    }else{
        return originalResponse.newBuilder()
                .header("Cache-Control", "public, only-if-cached, max-stale=2419200")
                .removeHeader("Pragma")
                .build();
    }
};

最後日誌攔截器也貼上來吧

private final Interceptor LoggingInterceptor = chain -> { 
    Request request = chain.request(); 
    long t1 = System.nanoTime();
    Logger.t(TAG).i(String.format("Sending request %s on %s%n%s", request.url(),  chain.connection(), request.headers()));
    Response response = chain.proceed(request); 
    long t2 = System.nanoTime(); 
    Logger.t(TAG).i(String.format("Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers())); 
    return response; 
};

如下測試Cache-Control的配置在請求頭和響應頭中都有且同樣。

max-stale在請求頭設置有效,在響應頭設置無效。
max-stale和max-age同時設置的時候,緩存失效的時間按最長的算。
關於max-age和max-stale我這裏作了一個測試:
測試結果:
我在請求頭中設置了:Cache-Control: public, max-age=60,max-stale=120,響應頭的Cache-Control和請求頭同樣。

  • 在第一次請求數據到一分鐘以內,響應頭有:Cache-Control: public, max-age=60,max-stale=120
  • 在1分鐘到3分鐘在之間,響應頭有:Cache-Control: public, max-age=60,max-stale=120
    Warning: 110 HttpURLConnection "Response is stale"
    能夠發現多了一個Warning。
  • 三分鐘的時候:從新請求了數據,如此循環,若是到了從新請求的節點此時沒有網,則請求失敗。

另外關於緩存有一個rxcache也能夠試試。



文/never615(簡書做者)


相關文章
相關標籤/搜索