【Android】使用Retrofit/OkHttpClient時的緩存詳解

okhttp.jpg

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

  • 1.幫app端用戶減少流量消耗(因為很多情況下躬窜,請求網(wǎng)絡(luò)返回的response并沒有變化)执解,同時提升用戶體驗,可以在沒有網(wǎng)絡(luò)的情況下也可以查看上次的數(shù)據(jù);
  • 2.根據(jù)業(yè)務(wù)場景渔彰,設(shè)置不同的緩存時間,app端的用戶體驗提高推正,穩(wěn)定性能提高恍涂;
  • 3.通過減少很多不必要的http請求,減輕服務(wù)器負(fù)載植榕,同時也可以減少服務(wù)端消耗的數(shù)據(jù)流量再沧。

本文demo地址

常用的緩存方式:

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

具體通過代碼來講解:

if (retrofit == null) {
                    retrofit=new Retrofit.Builder().baseUrl("http://gank.io/api/")
                            .addConverterFactory(GsonConverterFactory.create())
                            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                            //偷個懶直接寫一起
                            .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單例添加了緩存機制,最大緩存空間為10Mb技俐,應(yīng)用攔截與網(wǎng)絡(luò)攔截也采用了同一個攔截器乘陪,簡單區(qū)別就是

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

Interceptor的具體實現(xiàn)也很簡單仇穗,只需要重寫一個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ò)不會使用緩存數(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ù)據(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ò)耘子,對返回的數(shù)據(jù)response做了一個攔截處理果漾,其中需要注意的是

  • removeHeader();去除相關(guān)Cache-Control(緩存控制)的HTTP頭信息谷誓,不用去管绒障,一般都是固定的格式;
  • int maxAge = 10捍歪; 該值表示有網(wǎng)絡(luò)的時候户辱,希望在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中實際調(diào)試發(fā)現(xiàn),該值并沒有什么軟用乔遮,即使你設(shè)置為0蚂踊,無網(wǎng)絡(luò)時也會使用緩存數(shù)據(jù)。原因在這里:
            //無網(wǎng)絡(luò)時強制使用緩存數(shù)據(jù)
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();

追蹤CacheControl.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();

這樣锤窑,一個簡單的緩存方式就完成了璧针,看下demo效果,當(dāng)我在10S內(nèi)去不停地獲取Gank.io中圖片時渊啰,返回的一直是同一張探橱,僅當(dāng)超過設(shè)置的maxAge時間后,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

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

cache_file.png
Cookie緩存:

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

.cookieJar(new NovateCookieManger(context))

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

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

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

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

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

不錯的參考資料:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肠缨,一起剝皮案震驚了整個濱河市逆趋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晒奕,老刑警劉巖闻书,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脑慧,居然都是意外死亡魄眉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門闷袒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坑律,“玉大人,你說我怎么就攤上這事囊骤』卧瘢” “怎么了冀值?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宫屠。 經(jīng)常有香客問我列疗,道長,這世上最難降的妖魔是什么浪蹂? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任抵栈,我火速辦了婚禮,結(jié)果婚禮上乌逐,老公的妹妹穿的比我還像新娘竭讳。我一直安慰自己,他們只是感情好浙踢,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布绢慢。 她就那樣靜靜地躺著,像睡著了一般洛波。 火紅的嫁衣襯著肌膚如雪胰舆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天蹬挤,我揣著相機與錄音缚窿,去河邊找鬼。 笑死焰扳,一個胖子當(dāng)著我的面吹牛倦零,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吨悍,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扫茅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了育瓜?” 一聲冷哼從身側(cè)響起葫隙,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎躏仇,沒想到半個月后恋脚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡焰手,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年糟描,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片书妻。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡船响,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情灿意,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布崇呵,位于F島的核電站缤剧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏域慷。R本人自食惡果不足惜荒辕,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望犹褒。 院中可真熱鬧抵窒,春花似錦、人聲如沸叠骑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宙枷。三九已至掉房,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間慰丛,已是汗流浹背卓囚。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诅病,地道東北人哪亿。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像贤笆,于是被迫代替她去往敵國和親蝇棉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容