使用Retrofit和Okhttp實(shí)現(xiàn)網(wǎng)絡(luò)緩存。無(wú)網(wǎng)讀緩存,有網(wǎng)根據(jù)過(guò)期時(shí)間重新請(qǐng)求

使用Retrofit和Okhttp實(shí)現(xiàn)網(wǎng)絡(luò)緩存,更新于2016.02.02


本文使用 Retrofit2.0.0-beta2、Okhttp 2.6.0(Okhttp3.0之后api寫(xiě)法有變化)

  • 配置Okhttp的Cache
  • 配置請(qǐng)求頭中的cache-control或者統(tǒng)一處理所有請(qǐng)求的請(qǐng)求頭
  • 云端配合設(shè)置響應(yīng)頭或者自己寫(xiě)攔截器修改響應(yīng)頭中cache-control

最后實(shí)現(xiàn)的效果是:有網(wǎng)的時(shí)候根據(jù)你每個(gè)接口設(shè)置的需要緩存的時(shí)間(1分鐘、5分鐘等)進(jìn)行緩存,過(guò)了時(shí)間重新請(qǐng)求;沒(méi)網(wǎng)的時(shí)候讀緩存。

在這里插一句為什么要做緩存,或者說(shuō)有什么好處?
減少服務(wù)器負(fù)荷,降低延遲提升用戶(hù)體驗(yàn)。復(fù)雜的緩存策略會(huì)根據(jù)用戶(hù)當(dāng)前的網(wǎng)絡(luò)情況采取不同的緩存策略,比如在2g網(wǎng)絡(luò)很差的情況下,提高緩存使用的時(shí)間;不用的應(yīng)用、業(yè)務(wù)需求、接口所需要的緩存策略也會(huì)不一樣,有的要保證數(shù)據(jù)的實(shí)時(shí)性,所以不能有緩存,有的你可以緩存5分鐘,等等。你要根據(jù)具體情況所需數(shù)據(jù)的時(shí)效性情況給出不同的方案。當(dāng)然你也可以全部都一樣的緩存策略,看你自己。

1.配置okhttp中的Cache

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

2.配置請(qǐng)求頭中的cache-control

緩存的相關(guān)知識(shí)和參數(shù)的說(shuō)明,我是個(gè)鏈接1
緩存的相關(guān)知識(shí)和參數(shù)的說(shuō)明,我是個(gè)鏈接2

在Retrofit中,我們可以通過(guò)@Headers來(lái)配置,如:

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

沒(méi)有設(shè)置的可以即為有網(wǎng)的時(shí)候不進(jìn)行緩存。

或者你所有接口在有網(wǎng)的時(shí)候都不需要緩存或者都需要緩存且時(shí)間一樣,那么也不用配置每個(gè)接口的@Headers的Cache-Control了。

3.云端配合設(shè)置響應(yīng)頭或者自己寫(xiě)攔截器修改響應(yīng)頭response中cache-control

到這一步緩存就已經(jīng)待在你的緩存目錄了。
如果云端有處里cache的話(huà),就已經(jīng)可以了。
但是很可能云端沒(méi)有處理,所以返回的響應(yīng)頭中cache-control是no-cache,這時(shí)候你還是無(wú)法做緩存,大家可以用okhttp的寫(xiě)日志攔截器查看響應(yīng)頭的內(nèi)容。

[ Okhttp Interceptors 使用說(shuō)明,我是個(gè)鏈接](https://github.com/square/okhttp/wiki/Interceptors" target="_blank)

如果云端現(xiàn)在不方便處理的話(huà),你也可以自己搞定緩存的,那就是寫(xiě)攔截器修改響應(yīng)頭中的cache-control。我把請(qǐng)求頭中的cache-control讀出來(lái)然后設(shè)置到了響應(yīng)頭中。

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

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

攔截器如下:云端響應(yīng)頭攔截器,用來(lái)配置緩存策略

/**
 * 云端響應(yīng)頭攔截器,用來(lái)配置緩存策略
 * 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)){
        //有網(wǎng)的時(shí)候讀接口上的@Headers里的配置,你可以在這里進(jìn)行統(tǒng)一的設(shè)置
        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();
    }
};

最后日志攔截器也貼上來(lái)吧

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; 
};


以下測(cè)試Cache-Control的配置在請(qǐng)求頭和響應(yīng)頭中都有且一樣。

max-stale在請(qǐng)求頭設(shè)置有效,在響應(yīng)頭設(shè)置無(wú)效。(因?yàn)閙ax-stale是請(qǐng)求頭設(shè)置參數(shù),參考上面的緩存相關(guān)的知識(shí)第二個(gè)鏈接)
max-stale和max-age同時(shí)設(shè)置的時(shí)候,緩存失效的時(shí)間按最長(zhǎng)的算。
關(guān)于max-age和max-stale我這里做了一個(gè)測(cè)試:
測(cè)試結(jié)果:
我在請(qǐng)求頭中設(shè)置了:Cache-Control: public, max-age=60,max-stale=120,響應(yīng)頭的Cache-Control和請(qǐng)求頭一樣。

  • 在第一次請(qǐng)求數(shù)據(jù)到一分鐘之內(nèi),響應(yīng)頭有:Cache-Control: public, max-age=60,max-stale=120
  • 在1分鐘到3分鐘在之間,響應(yīng)頭有:Cache-Control: public, max-age=60,max-stale=120
    Warning: 110 HttpURLConnection "Response is stale"
    可以發(fā)現(xiàn)多了一個(gè)Warning。
  • 三分鐘的時(shí)候:重新請(qǐng)求了數(shù)據(jù),如此循環(huán),如果到了重新請(qǐng)求的節(jié)點(diǎn)此時(shí)沒(méi)有網(wǎng),則請(qǐng)求失敗。

另外關(guān)于緩存有一個(gè)rxcache也可以試試。

感謝@Picasso_L一起討論研究

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 在Android開(kāi)發(fā)中我們經(jīng)常要進(jìn)行各種網(wǎng)絡(luò)訪(fǎng)問(wèn),比如查看各類(lèi)新聞、查看各種圖片。但有一種情形就是我們每次重...
    SnowDragonYY閱讀 6,426評(píng)論 2 11
  • 本文就是講解在OKHTTP中如何配置緩存。 HTTP協(xié)議中緩存相關(guān) 為了更好的講解OKHTTP怎么設(shè)置緩存,我們追...
    TendaZhang閱讀 3,117評(píng)論 7 19
  • 1.OkHttp源碼解析(一):OKHttp初階 2 OkHttp源碼解析(二):OkHttp連接的"前戲"——H...
    隔壁老李頭閱讀 8,460評(píng)論 12 43
  • Okhttp使用指南與源碼分析 標(biāo)簽(空格分隔): Android 使用指南篇# 為什么使用okhttp### A...
    背影殺手不太冷閱讀 8,284評(píng)論 2 119
  • 浮動(dòng)元素有什么特征?對(duì)父容器、其他浮動(dòng)元素、普通元素、文字分別有什么影響? 浮動(dòng)元素的特征:1、脫離普通文檔流;2...
    Jeff12138閱讀 364評(píng)論 0 0

友情鏈接更多精彩內(nèi)容