【Android】使用Retrofit/OkHttpClient時(shí)的緩存詳解

okhttp.jpg

首選說下個(gè)人覺得網(wǎng)絡(luò)緩存控制的優(yōu)勢(shì):

  • 1.幫app端用戶減少流量消耗(因?yàn)楹芏嗲闆r下,請(qǐng)求網(wǎng)絡(luò)返回的response并沒有變化),同時(shí)提升用戶體驗(yàn),可以在沒有網(wǎng)絡(luò)的情況下也可以查看上次的數(shù)據(jù);
  • 2.根據(jù)業(yè)務(wù)場景,設(shè)置不同的緩存時(shí)間,app端的用戶體驗(yàn)提高,穩(wěn)定性能提高;
  • 3.通過減少很多不必要的http請(qǐng)求,減輕服務(wù)器負(fù)載,同時(shí)也可以減少服務(wù)端消耗的數(shù)據(jù)流量。

本文demo地址

常用的緩存方式:

如果你的網(wǎng)路請(qǐng)求框架是Retrofit或OkHttpClient,那么只需要通過設(shè)置cache和Interceptor即可,cache的作用是指定緩存文件地址、大小,Interceptor稍微復(fù)雜一些,分為應(yīng)用攔截、網(wǎng)絡(luò)攔截兩種,主要控制緩存的時(shí)間、方式、過濾等。

具體通過代碼來講解:

if (retrofit == null) {
                    retrofit=new Retrofit.Builder().baseUrl("http://gank.io/api/")
                            .addConverterFactory(GsonConverterFactory.create())
                            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                            //偷個(gè)懶直接寫一起
                            .client(new OkHttpClient.Builder()
                                    .cache(new Cache(new File(context.getExternalCacheDir(),"test_cache"),10 * 1024 * 1024))
                                    .addInterceptor(new CaheInterceptor(context))
                                    .addNetworkInterceptor(new CaheInterceptor(context))
                                    .connectTimeout(5, TimeUnit.SECONDS)
                                    .build())
                            .build();
                }

上述代碼就為retrofit單例添加了緩存機(jī)制,最大緩存空間為10Mb,應(yīng)用攔截與網(wǎng)絡(luò)攔截也采用了同一個(gè)攔截器,簡單區(qū)別就是

addNetworkInterceptor添加的是網(wǎng)絡(luò)攔截器,他會(huì)在在request和resposne是分別被調(diào)用一次,
能夠操作中間過程的響應(yīng),如重定向和重試;
而addinterceptor添加的是aplication攔截器,他只會(huì)在response被調(diào)用一次,
且總是只調(diào)用一次,不需要擔(dān)心中間過程的響應(yīng)。

Interceptor的具體實(shí)現(xiàn)也很簡單,只需要重寫一個(gè)intercept方法,在其中處理多種緩存狀態(tài),例有網(wǎng)絡(luò),無網(wǎng)絡(luò),網(wǎng)絡(luò)狀態(tài)差等等。

@Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (NetworkUtils.isNetworkAvailable(context)) {
            Response response = chain.proceed(request);
            // read from cache for 0 s  有網(wǎng)絡(luò)不會(huì)使用緩存數(shù)據(jù)
            int maxAge = 10;
            String cacheControl = request.cacheControl().toString();
            return response.newBuilder()
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, max-age=" + maxAge)
                    .build();
        } else {
            //無網(wǎng)絡(luò)時(shí)強(qiáng)制使用緩存數(shù)據(jù)
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();
            Response response = chain.proceed(request);
            //set cahe times ; value is useless at all !
//            int maxStale = 60;
            int maxStale = 60 * 60 * 24 * 3;
            return response.newBuilder()
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .build();
        }
    }

上面的代碼僅根據(jù)當(dāng)前有無網(wǎng)絡(luò),對(duì)返回的數(shù)據(jù)response做了一個(gè)攔截處理,其中需要注意的是

  • removeHeader();去除相關(guān)Cache-Control(緩存控制)的HTTP頭信息,不用去管,一般都是固定的格式;
  • int maxAge = 10; 該值表示有網(wǎng)絡(luò)的時(shí)候,希望在10S內(nèi)不去重新獲取網(wǎng)絡(luò)數(shù)據(jù),而是直接使用緩存數(shù)據(jù);
  • int maxStale = 60 * 60 * 24 * 3; 表示在無網(wǎng)絡(luò)的情況下,在3天內(nèi)使用緩存數(shù)據(jù)?但是在demo中實(shí)際調(diào)試發(fā)現(xiàn),該值并沒有什么軟用,即使你設(shè)置為0,無網(wǎng)絡(luò)時(shí)也會(huì)使用緩存數(shù)據(jù)。原因在這里:
            //無網(wǎng)絡(luò)時(shí)強(qiáng)制使用緩存數(shù)據(jù)
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();

追蹤C(jī)acheControl.FORCE_CACHE型的cacheControl源碼,發(fā)現(xiàn)其內(nèi)部,已經(jīng)將maxStale 設(shè)置為無限大了。

  public static final CacheControl FORCE_CACHE = new Builder()
      .onlyIfCached()
      .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
      .build();

這樣,一個(gè)簡單的緩存方式就完成了,看下demo效果,當(dāng)我在10S內(nèi)去不停地獲取Gank.io中圖片時(shí),返回的一直是同一張,僅當(dāng)超過設(shè)置的maxAge時(shí)間后,picurl才更新了:

04-14 16:32:27.858 21905-21905/com.blink.dagger.democache D/luck: pic url :http://ww2.sinaimg.cn/large/7a8aed7bgw1ewees6m58qj20dw0kuadj.jpg
04-14 16:32:27.860 21905-21905/com.blink.dagger.democache D/luck: current time :2017-04-14 16:32:27
04-14 16:32:29.989 21905-21905/com.blink.dagger.democache D/luck: pic url :http://ww2.sinaimg.cn/large/7a8aed7bgw1ewees6m58qj20dw0kuadj.jpg
04-14 16:32:29.991 21905-21905/com.blink.dagger.democache D/luck: current time :2017-04-14 16:32:29
04-14 16:32:31.098 21905-21905/com.blink.dagger.democache D/luck: pic url :http://ww2.sinaimg.cn/large/7a8aed7bgw1ewees6m58qj20dw0kuadj.jpg
04-14 16:32:31.099 21905-21905/com.blink.dagger.democache D/luck: current time :2017-04-14 16:32:31
04-14 16:32:32.481 21905-21905/com.blink.dagger.democache D/luck: pic url :http://ww2.sinaimg.cn/large/7a8aed7bgw1ewees6m58qj20dw0kuadj.jpg
04-14 16:32:32.482 21905-21905/com.blink.dagger.democache D/luck: current time :2017-04-14 16:32:32
04-14 16:32:34.327 21905-21905/com.blink.dagger.democache D/luck: pic url :http://ww2.sinaimg.cn/large/7a8aed7bgw1ewees6m58qj20dw0kuadj.jpg
04-14 16:32:34.328 21905-21905/com.blink.dagger.democache D/luck: current time :2017-04-14 16:32:34
04-14 16:32:36.865 21905-21905/com.blink.dagger.democache D/luck: pic url :http://ww1.sinaimg.cn/large/7a8aed7bjw1ezf3wrxcx2j20p011i7b2.jpg
04-14 16:32:36.867 21905-21905/com.blink.dagger.democache D/luck: current time :2017-04-14 16:32:36

而我們的手機(jī)ExternalCacheDir路徑下,多了一些二進(jìn)制文件,所以如果需要做一個(gè)清除緩存的功能,在業(yè)務(wù)代碼中delete file即可。

cache_file.png
Cookie緩存:

一般的場景應(yīng)該都用不到這種緩存方式,僅在每一次請(qǐng)求訪問網(wǎng)絡(luò)的時(shí)候,需要根據(jù)url將Cookie中的緩存數(shù)據(jù)提交給后臺(tái)時(shí)才用的著。
使用方法:

.cookieJar(new NovateCookieManger(context))

而且要實(shí)現(xiàn)CookieJar非常麻煩:

  • 1.首選需要根據(jù)httpurl保存/發(fā)送Cookie(實(shí)現(xiàn)CookieJar接口);
  • 2.將Map形式的cookies緩存至內(nèi)存中;
  • 3.將cookies序列化后通過SP持久化至本地;

好在這種反人類的緩存方式一般不用。

Cookie難以被緩存,且大多情境下是沒有必要的。如果你非得使用Cookie,建議用在動(dòng)態(tài)頁面上。

具體的代碼太長了,這兒就不貼了,直接放在demo中了。

不錯(cuò)的參考資料:

Web 開發(fā)人員需知的 Web 緩存知識(shí)
OkHttp3 (四)——Cookie與攔截器

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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