OKHttp3 系列 — 攔截器的使用

2018年4月17日更新
在看okthttp3源碼的時(shí)候發(fā)現(xiàn)构资,這邊文章最底下的總結(jié)內(nèi)容中的第一點(diǎn):
無網(wǎng)絡(luò)請(qǐng)求下呵恢,okhttp不會(huì)走入攔截器中呵哨,所以在這里面編寫無網(wǎng)絡(luò)代碼邏輯是無效的刹前;
是錯(cuò)誤的結(jié)論毛嫉,因?yàn)閿r截器是鏈?zhǔn)綀?zhí)行的袱饭,所以每個(gè)攔截器中的chain.proceed(request);方法其實(shí)是在調(diào)用執(zhí)行下一個(gè)攔截器的intercept()方法川无,于是攔截器中返回的response就是從最底層攔截器開始一層一層的進(jìn)行封裝,然后原路返回到最上一層攔截器中虑乖。所以沒有網(wǎng)絡(luò)的情況下還是會(huì)執(zhí)行攔截器懦趋,只是說,我打印日志的位置因?yàn)樵?code>chain.proceed(request);方法后所以沒有任何打印結(jié)果疹味,因?yàn)閳?zhí)行這里的時(shí)候仅叫,沒有網(wǎng)絡(luò)就已經(jīng)報(bào)了網(wǎng)絡(luò)異常。

在此糙捺,如給大家造成了誤解诫咱,見諒見諒!


最近實(shí)在太忙洪灯,所以影響了更新速度...

添加Interceptor

在上一篇中我們已經(jīng)知道了okhttp的基本使用遂跟,其中在介紹OkHttpClient初始化的時(shí)候,介紹了兩種方式婴渡,第二種方式就可以對(duì)這個(gè)OkHttpClient對(duì)象設(shè)置攔截器幻锁,如下所示:

// 配置一些信息進(jìn)入OkHttpClient
mOkHttpClient = new OkHttpClient().newBuilder()
                .connectTimeout(REQUEST_TIME, TimeUnit.SECONDS)
                .readTimeout(REQUEST_TIME, TimeUnit.SECONDS)
                .writeTimeout(REQUEST_TIME, TimeUnit.SECONDS)
                .addInterceptor(new LoggerInterceptor())
                .build();

如上代碼,很簡(jiǎn)單边臼,只要利用addInterceptor方法就可以添加攔截器哄尔,而自定義的攔截器只需要實(shí)現(xiàn)Interceptor接口就行了,如下所示:

public class LoggerInterceptor implements Interceptor {
      ...
}

應(yīng)用場(chǎng)景

日志打印

可以使用攔截器方便的打印網(wǎng)絡(luò)請(qǐng)求時(shí)柠并,需要查看的日志岭接。如下所示:

public class LoggerInterceptor implements Interceptor {

    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        // 攔截請(qǐng)求富拗,獲取到該次請(qǐng)求的request
        Request request = chain.request();
        // 執(zhí)行本次網(wǎng)絡(luò)請(qǐng)求操作,返回response信息
        Response response = chain.proceed(request);
        if (Configuration.DEBUG) {
            for (String key : request.headers().toMultimap().keySet()) {
                LogUtil.e("zp_test", "header: {" + key + " : " + request.headers().toMultimap().get(key) + "}");
            }
            LogUtil.e("zp_test", "url: " + request.url().uri().toString());
            ResponseBody responseBody = response.body();

            if (HttpHeaders.hasBody(response) && responseBody != null) {
                BufferedReader bufferedReader = new BufferedReader(new
                        InputStreamReader(responseBody.byteStream(), "utf-8"));
                String result;
                while ((result = bufferedReader.readLine()) != null) {
                    LogUtil.e("zp_test", "response: " + result);
                }
                // 測(cè)試代碼
                responseBody.string();
            }
        }
        // 注意鸣戴,這樣寫啃沪,等于重新創(chuàng)建Request,獲取新的Response窄锅,避免在執(zhí)行以上代碼時(shí)创千,
        // 調(diào)用了responseBody.string()而不能在返回體中再次調(diào)用。
        return response.newBuilder().build();
    }

}

做了一個(gè)打印驗(yàn)證:通過分別打印攔截器與返回體的時(shí)間和線程名字入偷,可以知道這兩者處于同一線程中追驴,增加攔截器疏之,請(qǐng)求執(zhí)行的時(shí)間也會(huì)增加,所以猜測(cè)锋爪,其實(shí)就是線性的在執(zhí)行不同攔截器中的代碼,根據(jù)需求返回一個(gè)相同的或者新的response其骄。

緩存

想要實(shí)現(xiàn)緩存亏镰,先在創(chuàng)建okhttpclint的時(shí)候多加一行代碼.cache(),通過它來設(shè)置緩存目錄年栓,當(dāng)然需要服務(wù)器支持緩存功能薄霜。

 mOkHttpClient = new OkHttpClient().newBuilder()
                .cache(new Cache(FileUtils.getCacheDirectory(AppApplication
                        .getApplication(), ""), 1024 * 1024))
                .connectTimeout(REQUEST_TIME, TimeUnit.SECONDS)
                .readTimeout(REQUEST_TIME, TimeUnit.SECONDS)
                .writeTimeout(REQUEST_TIME, TimeUnit.SECONDS)
                .addNetworkInterceptor(new LoggerInterceptor())
                .build();

如果服務(wù)器端支持緩存的話,則請(qǐng)求所返回的Response會(huì)帶有這樣的頭信息header:cache-control, max-age=xxx,這樣設(shè)置惰瓜。這時(shí)可以直接使用緩存功能否副。其中,max-age設(shè)置的緩存時(shí)間崎坊,過了這個(gè)時(shí)間备禀,就算有緩存也不會(huì)進(jìn)行使用奈揍。

像我公司服務(wù)器返回的頭信息中與緩存相關(guān)的字段如下:
header: {cache-control : [no-store, private]}
header: {pragma : [no-cache]}

這就說明,服務(wù)器默認(rèn)是不支持緩存的男翰,okhttp就不會(huì)對(duì)此次請(qǐng)求進(jìn)行緩存。為了讓okhttp緩存此次響應(yīng)昆箕,就必須重新設(shè)置response的請(qǐng)求頭信息。

接下來再看攔截器中如何設(shè)置緩存請(qǐng)求頭信息鹏倘。

public class LoggerInterceptor implements Interceptor {

    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        // 攔截請(qǐng)求,獲取到該次請(qǐng)求的request
        Request request = chain.request();
        // 執(zhí)行本次網(wǎng)絡(luò)請(qǐng)求操作骆姐,返回response信息
        Response response = chain.proceed(request);
        if (Configuration.DEBUG) {
            for (String key : request.headers().toMultimap().keySet()) {
                LogUtil.e("zp_test", "header: {" + key + " : " + request.headers().toMultimap().get(key) + "}");
            }
            LogUtil.e("zp_test", "url: " + request.url().uri().toString());
            ResponseBody responseBody = response.body();
        }
      
        return response.newBuilder()
            // 增加一個(gè)緩存頭信息夕吻,緩存時(shí)間為60s
            .header("cache-control", "public, max-age=60")
             // 移除pragma頭信息
            .removeHeader("pragma")
            .build();
    }

}

這樣設(shè)置,就等于是在60s內(nèi)強(qiáng)制設(shè)置使用緩存涉馅。

注意點(diǎn):
切記,最開始庸诱,我一直在犯一個(gè)錯(cuò)誤晤揣,okhttp3不能緩存post接口

攔截器可以理解為,給請(qǐng)求的request和response重新一次封裝的機(jī)會(huì)昧识,使得你可以在特定條件下,給一些特定的接口或者滿足特定條件的接口一些特殊的操作缀去。

比如有一種場(chǎng)景,有網(wǎng)絡(luò)時(shí)缕碎,進(jìn)行請(qǐng)求池户,無網(wǎng)絡(luò)時(shí),拿緩存數(shù)據(jù)校焦。先看網(wǎng)上的一種方法。

if (NetUtils.isNetAvailable(AppApplication.getApplication())) {  
      response.newBuilder()  
               .header("Cache-Control", "public, max-age=" + 0)  
               .removeHeader("Pragma")  
               .build();  
 } else {  
      int maxStale = 60 * 60 * 24; // 無網(wǎng)絡(luò)時(shí)熏迹,設(shè)置超時(shí)為1天
      response.newBuilder()  
              .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)  
              .removeHeader("Pragma")  
              .build();  
 } 

return response;

max-stale:在max-age指定的失效時(shí)間外凝赛,額外增加一段指定的時(shí)間可以使用失效的response坛缕。

網(wǎng)上有很多是上面這種做法捆昏,但是,我在攔截器中試了一下骗卜,當(dāng)沒有網(wǎng)絡(luò)時(shí),壓根就不會(huì)走入攔截器举户。(我使用的是網(wǎng)絡(luò)攔截器,如果有是別的什么原因俭嘁,歡迎指出錯(cuò)誤)

最終解決方案是在初始化request(如果初始化不熟悉可以參考我的上一篇文章OKHttp3的基本使用)的時(shí)候進(jìn)行的判斷操作服猪,當(dāng)有網(wǎng)絡(luò)時(shí)初始化正常的request,當(dāng)沒有網(wǎng)絡(luò)時(shí)初始化強(qiáng)制使用緩存的request:

Request request;
if (NetUtils.isNetAvailable(AppApplication.getApplication())) {
      request = addHeaderInfo().url(requestUrl).build();
} else {
      request = addHeaderInfo().url(requestUrl).cacheControl(CacheControl.FORCE_CACHE).build();
}

攔截器還是使用上面的那種形式近她,只是將有效時(shí)間變成了0膳帕,主要是為了在有網(wǎng)絡(luò)情況下每次都請(qǐng)求最新的數(shù)據(jù)粘捎。

response.newBuilder()  
             .header("Cache-Control", "public, max-age=" + 0)  
             .removeHeader("Pragma")  
             .build();  

這樣就可以在有網(wǎng)絡(luò)的情況下使用最新的數(shù)據(jù)攒磨,在無網(wǎng)絡(luò)的情況下使用緩存數(shù)據(jù)恬砂。

總結(jié)

第一點(diǎn)咧纠,無網(wǎng)絡(luò)請(qǐng)求下泻骤,okhttp不會(huì)走入攔截器中梧奢,所以在這里面編寫無網(wǎng)絡(luò)代碼邏輯是無效的;
第二點(diǎn)趋惨,無網(wǎng)絡(luò)情況下惦蚊,通過給request設(shè)置一個(gè)強(qiáng)制緩存標(biāo)志:CacheControl.FORCE_CACHE來告訴okhttp本次請(qǐng)求走緩存讯嫂,并且還得在之前的網(wǎng)絡(luò)請(qǐng)求中已經(jīng)緩存了數(shù)據(jù)到本地兆沙。如果此時(shí)沒有命中緩存文件,則會(huì)報(bào)504葛圃;
第三點(diǎn),有網(wǎng)絡(luò)情況下库正,咱們可以利用攔截器和服務(wù)器的緩存策略進(jìn)行動(dòng)態(tài)配合。

最后歡迎大家提問一起討論龙誊,也歡迎指出文中錯(cuò)誤属瓣,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末护昧,一起剝皮案震驚了整個(gè)濱河市粗截,隨后出現(xiàn)的幾起案子惋耙,更是在濱河造成了極大的恐慌熊昌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灭美,死亡現(xiàn)場(chǎng)離奇詭異昂利,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)犁苏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門扩所,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人祖屏,你說我怎么就攤上這事买羞”⑹常” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵漠嵌,是天一觀的道長(zhǎng)盖呼。 經(jīng)常有香客問我,道長(zhǎng)几晤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任圾浅,我火速辦了婚禮憾朴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘众雷。我一直安慰自己,他們只是感情好鸡岗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布编兄。 她就那樣靜靜地躺著,像睡著了一般狠鸳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舒岸,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天芦圾,我揣著相機(jī)與錄音俄认,去河邊找鬼洪乍。 笑死夜焦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茫经。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抹镊,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼荤傲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起遂黍,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤雾家,失蹤者是張志新(化名)和其女友劉穎铃彰,沒想到半個(gè)月后芯咧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鹃共,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年驶拱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阴孟。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡税迷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箭养,到底是詐尸還是另有隱情,我是刑警寧澤喝检,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站挠说,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏损俭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一外永、第九天 我趴在偏房一處隱蔽的房頂上張望拧咳。 院中可真熱鬧,春花似錦骆膝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至养交,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碎连,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工廉嚼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留倒戏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓傍念,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親捂寿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子孵运,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355