Android okhttp緩存真正正確的實現方式

前言

關於okhttp的緩存,網上有大量的文章,或相同,或不一樣,方式不一,但都八九不離十,原理都是經過CacheControl的設置策略不一樣來實現的。 可是,真正實踐過的人會發現,好像有這樣那樣的問題。 好比:html

  • 究竟是用addNetInterceptor呢仍是用addInterceptor,不一樣的用法有不一樣的效果
  • 什麼有網的時候是maxAge,無網的時候又是maxStale等等

簡直不明白。 因而乎,我我的作了不少不少的嘗試,幾乎把網上的方法都試了一遍,看了大量換湯不換藥的文章。下面是借鑑文章出處,可是個人方法都與他們的不一樣。android

blog.csdn.net/u014614038/… blog.csdn.net/Picasso_L/a… blog.csdn.net/briblue/art… www.jianshu.com/p/412157e23… stackoverflow.com/questions/2… newfivefour.com/android-ret…數據庫

對於okhttp的緩存解決方案,個人需求是:

一、有網的時候也能夠讀取緩存,而且能夠控制緩存的過時時間,這樣能夠減輕服務器壓力 二、有網的時候不讀取緩存,好比一些及時性較高的接口請求 三、無網的時候讀取緩存,而且能夠控制緩存過時的時間緩存

正文

說了那麼多,先直接上解決方案bash

/**
     * 有網時候的緩存
     */
    final Interceptor NetCacheInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);
            int onlineCacheTime = 30;//在線的時候的緩存過時時間,若是想要不緩存,直接時間設置爲0
            return response.newBuilder()
                    .header("Cache-Control", "public, max-age="+onlineCacheTime)
                    .removeHeader("Pragma")
                    .build();
        }
    };
    /**
     * 沒有網時候的緩存
     */
    final Interceptor OfflineCacheInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (!SystemTool.checkNet(AppContext.context)) {
                int offlineCacheTime = 60;//離線的時候的緩存的過時時間
                request = request.newBuilder()
//                        .cacheControl(new CacheControl
//                                .Builder()
//                                .maxStale(60,TimeUnit.SECONDS)
//                                .onlyIfCached()
//                                .build()
//                        ) 兩種方式結果是同樣的,寫法不一樣
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + offlineCacheTime)
                        .build();
            }
            return chain.proceed(request);
        }
    };

  //setup cache
    File httpCacheDirectory = new File(AppContext.context.getCacheDir(), "okhttpCache");
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache cache = new Cache(httpCacheDirectory, cacheSize);
    OkHttpClient client = new OkHttpClient.Builder()
            .addNetworkInterceptor(NetCacheInterceptor)
            .addInterceptor(OfflineCacheInterceptor)
            .cache(cache)
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .build();

複製代碼

沒錯最終的解決方案就是,兩個interceptor,而且針對不一樣的網絡狀況進行不一樣的處理。(爲何是兩個interceptor後面會講到)服務器

若是趕時間的朋友,能夠直接拿去用,看到這裏就好了。若是以爲用起來就問題,歡迎下面留言。網絡

實踐和測試的過程

首先我最初的寫法就是來源於網上大多數人的寫法,相似ide

public class HttpCacheInterceptor implements Interceptor {

@Override
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    if (!NetWorkHelper.isNetConnected(MainApplication.getContext())) {
        request = request.newBuilder()
                .cacheControl(CacheControl.FORCE_CACHE)
                .build();
    }

    Response response = chain.proceed(request);

    if (NetWorkHelper.isNetConnected(MainApplication.getContext())) {
        int maxAge = 60 * 60; // read from cache for 1 minute
        response.newBuilder()
                .removeHeader("Pragma")
                .header("Cache-Control", "public, max-age=" + maxAge)
                .build();
    } else {
        int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
        response.newBuilder()
                .removeHeader("Pragma")
                .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                .build();
    }
    return response;
  }
}

  //設置緩存100M
        Cache cache = new Cache(new File(MainApplication.getContext().getCacheDir(),"httpCache"),1024 * 1024 * 100);
        return new OkHttpClient.Builder()
            .cache(cache)
            .addNetworkInterceptor(new HttpCacheInterceptor())
            .build();
複製代碼

這段代碼知足不了個人需求,而且會發現,有些狀況離線的時候緩存過時的時間不可靠,有些狀況在線的時候緩存不可用。對於個人需求不可兼得。(故網上有人還分了2種不一樣狀況的寫法來針對不一樣的需求,但我想,爲何不綜合起來呢?)測試

對於這段代碼疑惑點: 一、max-age是啥,maxStale是啥,他們的區別是啥? 二、爲何沒有網絡的狀況下,request要cacheControl.FORCE_CACHE 三、爲何又要對response設置header的cache-control,到底request的設置跟response的設置有什麼區別? 四、addNetInterceptor和addInterceptor有什麼區別?ui

解答: 一、max-age是啥,maxStale是啥,他們的區別是啥?

maxAge和maxStale的區別在於: maxAge:沒有超出maxAge,無論怎麼樣都是返回緩存數據,超過了maxAge,發起新的請求獲取數據更新,請求失敗返回緩存數據。 maxStale:沒有超過maxStale,無論怎麼樣都返回緩存數據,超過了maxStale,發起請求獲取更新數據,請求失敗返回失敗

二、爲何沒有網絡的狀況下,request要cacheControl.FORCE_CACHE

public static final CacheControl FORCE_CACHE = new Builder() .onlyIfCached() .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS) .build(); 能夠看到FORCE_CACHE是設置了maxStale的最大時間爲interger的最大時間,因此,意思就是不管如何,都不會超過這個時間,因此就是一直(強制)拿緩存,也是想要實現緩存的正確邏輯

通常控制緩存有兩種方式: 一、在request裏面去設置cacheControl()策略 二、在header裏面去添加cache-control

後面也會看到,在request裏面設置header的cache-control和調用cacheControl方法來設置實際上是同樣的,咱們就是經過在request裏面來控制無網緩存的maxStale過時時間的

三、爲何又要對response設置header的cache-control,到底request的設置跟response的設置有什麼區別?

其實我到如今都尚未搞清楚,爲啥那些人要這樣寫,只是後面我在測試的過程當中推斷出來,有網的時候和無網的時候對於interceptor的調用是不一樣的,產生的結果也是不一樣的。好比request設置的時候就對無網緩存及其時間控制有效,response就不行

四、addNetInterceptor和addInterceptor有什麼區別?

addNetInterceptor是添加網絡攔截器,addInterceptor是添加應用攔截器,若是看到okhttp的流程分析的知道:應用攔截器是在網絡攔截器前執行的。

若是我使用的是addNetInterceptor: 一、有網的狀況下,能夠在期限內拿到緩存,而沒有去請求接口(經過測試數據庫的數據改動來判斷的) 二、沒有網的狀況下,直接就ConnectException了,根本不會走到interceptor裏面去了。(網上不少人都提出了這樣的問題)

若是我使用的是addInterceptor: 一、有網的狀況下,明明設置的是60秒,可是每次都沒有去拿緩存而都是請求的接口。(經過測試數據庫的數據改動來判斷的) 二、沒有網的狀況下,能夠拿到緩存數據(猜測:多是由於應用攔截器在網絡攔截器前執行,沒有網的狀況下,自己就執行不到網絡攔截器裏面去),可是緩存過時時間是「永久」,由於FORCE_CACHE裏面已經設置爲了integer的最大值,21億秒左右,堪稱永久 可是,依舊沒有辦法控制無網時候的緩存過時時間

面對這些個問題,我也是很無奈。只得不停地嘗試,不停地摸索。因而就在嘗試的過程當中發現了它的一些規則,因而最終寫出了一個自認爲「萬全」的方法。

  • 既然想要有網的狀況下拿緩存,那麼就須要addNetInterceptor,若是須要無網的狀況下拿緩存,就須要addInterceptor,因此不如直接作兩個interceptor吧!
  • 另外,若是想要控制有網的時候不去讀取緩存,能夠直接經過在response裏設置maxAge=0來實現。
  • 這裏經過大量實驗發現,只有在request去設置其maxStale才能控制無網時候的緩存時間,在response裏面去控制是不行的!
延伸

若是無網的時候,stale緩存時間過了,會怎麼樣呢? 會報504錯誤(屬於正常的邏輯)。

最後

歡迎留言,歡迎提問題、交流。

相關文章
相關標籤/搜索