摘要:如表示可接受過期小時內(nèi)的數(shù)據(jù)表示指定時間內(nèi)的緩存數(shù)據(jù)仍有效,與緩存是否過期無關(guān)。所以需要對的緩存過程進行干預(yù),使其滿足我們的需求。將對修改和緩存策略的攔截器應(yīng)用于設(shè)置緩存路徑和緩存容量接下來就可以在無網(wǎng)絡(luò)的情況下愉快地使用緩存數(shù)據(jù)了。
HTTP緩存
在Http協(xié)議中,緩存的控制是通過首部的Cache-Control來控制,通過對Cache-Control進行設(shè)置,即可實現(xiàn)不同的緩存策略。
Cache-Control和其他的首部字段一樣,使用key:value結(jié)構(gòu),同時value可有多個值, 值之間以,分隔(具體參考HTTP詳解)。Cache-Control是一個通用首部字段,在Http請求報文中可使用,也可在應(yīng)答報文中使用。
請求指令集(在請求報文中的取值):no-cache: 不要緩存數(shù)據(jù),直接從源服務(wù)器獲取數(shù)據(jù);
no-store: 不緩存請求或響應(yīng)的任何內(nèi)容;
max-age: 表示可接受過期過久的緩存數(shù)據(jù),同指定了參數(shù)的max-stale;
max-stale: 表示接收過期的緩存,如后面未指定參數(shù),則表示永遠接收緩存數(shù)據(jù)。如max-stale: 3600, 表示可接受過期1小時內(nèi)的數(shù)據(jù);
min-fresh: 表示指定時間內(nèi)的緩存數(shù)據(jù)仍有效,與緩存是否過期無關(guān)。如min-fresh: 60, 表示60s內(nèi)的緩存數(shù)據(jù)都有效,60s之后的緩存數(shù)據(jù)將無效。
only-if-cache: 表示直接獲取緩存數(shù)據(jù),若沒有數(shù)據(jù)返回,則返回504(Gateway Timeout)
應(yīng)答指令集(在應(yīng)答報文中的取值):public: 可向任一方提供緩存數(shù)據(jù);
private: 只向指定用戶提供緩存數(shù)據(jù);
no-cache: 緩存前需確認其有效性;
no-store: 不緩存請求或響應(yīng)的任何內(nèi)容;
max-age: 表示緩存的最大時間,在此時間范圍內(nèi),訪問該資源時,直接返回緩存數(shù)據(jù)。不需要對資源的有效性進行確認;
must-revalidate: 訪問緩存數(shù)據(jù)時,需要先向源服務(wù)器確認緩存數(shù)據(jù)是否有效,如無法驗證其有效性,則需返回504。需要注意的是:如果使用此值,則max-stale將無效。
更詳細內(nèi)容可參考:Http首部字段定義
了解了HTTP的理論知識,后面我們對OkHttp中的緩存進行簡單的介紹。
OkHttp攔截器OkHttp默認對Http緩存進行了支持,只要服務(wù)端返回的Response中含有緩存策略,OkHttp就會通過CacheInterceptor攔截器對其進行緩存。但是OkHttp默認情況下構(gòu)造的HTTP請求中并沒有加Cache-Control,即便服務(wù)器支持了,我們還是不能正常使用緩存數(shù)據(jù)。所以需要對OkHttp的緩存過程進行干預(yù),使其滿足我們的需求。
OkHttp的優(yōu)雅之處就在于使用了責(zé)任鏈模式,將請求-應(yīng)答過程中的每一步都通過一個攔截器來實現(xiàn),并對此過程的頭部和尾部都提供了擴展,這也為我們干預(yù)緩存過程提供了可能。所以在實現(xiàn)緩存之前,我們需要對OkHttp對攔截器的處理過程有個大概的了解。
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. Listinterceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); interceptors.add(retryAndFollowUpInterceptor); interceptors.add(new BridgeInterceptor(client.cookieJar())); interceptors.add(new CacheInterceptor(client.internalCache())); interceptors.add(new ConnectInterceptor(client)); if (!forWebSocket) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor(forWebSocket)); Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest, this, eventListener); return chain.proceed(originalRequest); }
以上代碼就是整個攔截器的處理過程,具體的流程可參考源碼,這里我們只說一下基本的流程:發(fā)起請求時,會按interceptors中加入的順序依次執(zhí)行,返回Response時按照逆序執(zhí)行:
自定義攔截器 <-> 內(nèi)置攔截器(retryAndFollowUpInterceptor...ConnectInterceptor) <-> 網(wǎng)絡(luò)攔截器 <-> CallServerInterceptor
其中CallServerInterceptor就是負責(zé)發(fā)送請求與接收應(yīng)答的攔截器。由于我們關(guān)注的只是緩存,所以只考慮內(nèi)置攔截器中的CacheInterceptor。那么流程可簡化為:
Request <-> 自定義攔截器 <-> CacheInterceptor <-> 網(wǎng)絡(luò)攔截器 <-> Response
從這個流程可以看出,如果服務(wù)端返回的Response中沒有Cache-Control, 那么我們可通過添加網(wǎng)絡(luò)攔截器來實現(xiàn)。同樣,在訪問緩存數(shù)據(jù)時,我們可通過添加自定義攔截器來實現(xiàn)。
使用OkHttp緩存在開始添加緩存策略之前,我們先了解一個完整的緩存策略:
整體來說,在有網(wǎng)絡(luò)的情況下,使用緩存還是比較復(fù)雜,這里我們通過簡化版的緩存策略(有網(wǎng)絡(luò)時訪問服務(wù)器,無網(wǎng)絡(luò)時返回緩存數(shù)據(jù))來演示OkHttp使用緩存的過程。
首先,我們通過定義一個網(wǎng)絡(luò)攔截器來為Response添加緩存策略:
public class HttpCacheInterceptor implements Interceptor { private Context context; public HttpCacheInterceptor(Context context) { this.context = context; } @Override public Response intercept(Chain chain) throws IOException { return chain.proceed(chain.request()).newBuilder() .request(newRequest) .removeHeader("Pragma") .header("Cache-Control", "public, max-age=" + 1) .build(); return response; } }
其次,通過自定義攔截器設(shè)置Request使用緩存的策略:
public class BaseInterceptor implements Interceptor { private Context mContext; public BaseInterceptor(Context context) { this.mContext = context; } @Override public Response intercept(Chain chain) throws IOException { if (NetworkUtil.isConnected(mContext)) { return chain.proceed(chain.request()); } else { // 如果沒有網(wǎng)絡(luò),則返回緩存未過期一個月的數(shù)據(jù) Request newRequest = chain.request().newBuilder() .removeHeader("Pragma") .header("Cache-Control", "only-if-cached, max-stale=" + 30 * 24 * 60 * 60); return chain.proceed(newRequest); } } }
Pragma是Http/1.1之前版本遺留的字段,用于做版本兼容,但不同的平臺對此有不同的實現(xiàn),所以在使用緩存策略時需要將其屏蔽,避免對緩存策略造成影響。
將對修改Request和Response緩存策略的攔截器應(yīng)用于OkHttp:
OkHttpClient httpClient = new OkHttpClient.Builder() .addInterceptor(new BaseInterceptor(context)) .addNetworkInterceptor(new HttpCacheInterceptor(context)) .cache(new Cache(context.getCacheDir(), 20 * 1024 * 1024)) // 設(shè)置緩存路徑和緩存容量 .build();
接下來就可以在無網(wǎng)絡(luò)的情況下愉快地使用緩存數(shù)據(jù)了。
不使用OkHttp的緩存如果覺得OkHttp的緩存太復(fù)雜,想自己來緩存數(shù)據(jù)怎么辦呢?有兩種方案來實現(xiàn):
自定義攔截器,
監(jiān)聽OkHttp的請求過程,在請求完成時緩存數(shù)據(jù);
自定義攔截器這種方案首先需要考慮應(yīng)使用普通的攔截器還是網(wǎng)絡(luò)攔截器,上面我們已經(jīng)了解了整個請求過程中攔截器的執(zhí)行順序,需要注意的是:在無網(wǎng)絡(luò)的情況下,請求在執(zhí)行到CacheIntercepter,如果沒有緩存數(shù)據(jù),將會直接返回,并不會執(zhí)行到自定義的網(wǎng)絡(luò)攔截器中,所以不適合在網(wǎng)絡(luò)攔截器中緩存數(shù)據(jù)。那么我們可通過自定義普通攔截器來實現(xiàn),基本的過程如下:
@Override // BaseInterceptor.java public Response intercept(Chain chain) throws IOException { Response response = null; if (NetworkUtil.isConnected(mContext)) { response = chain.proceed(newRequest); saveCacheData(response); // 保存緩存數(shù)據(jù) } else { // 不執(zhí)行chain.proceed會打斷責(zé)任鏈,即后面的攔截器不會被執(zhí)行 response = getCacheData(chain.request().url()); // 獲取緩存數(shù)據(jù) } return response; }監(jiān)聽OkHttp的請求過程
OkHttp: 使用這種方案你良心不會痛嗎?
這種方案可以說摒棄了OkHttp擴展攔截器這一強大的功能,直接與請求和應(yīng)答進行交互,基本的過程如下:
Request request = new Request.Builder() .url(realUrl) .build(); if (NetworkUtil.isConnected()) { httpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { // 返回緩存數(shù)據(jù) } @Override public void onResponse(Response response) throws IOException { // 1. 緩存數(shù)據(jù) // 2. 返回請求結(jié)果 } }); } else { // 返回緩存數(shù)據(jù) }優(yōu)缺點比較
這兩種方案都拋棄了OkHttp自己實現(xiàn)的緩存策略,所以更加靈活,尤其是監(jiān)聽OkHttp請求過程這種方法。但也都有一個很大的缺點:需要實現(xiàn)一個緩存模塊。在開發(fā)中具體使用哪種緩存策略,根據(jù)已有代碼模塊和需求衡量即可。
注意點對Response的緩存策略進行修改的攔截器一定要應(yīng)用于網(wǎng)絡(luò)攔截器,否則無法緩存數(shù)據(jù),因為在Response返回的過程中,普通的攔截器在內(nèi)置的CacheInterceptor之后執(zhí)行;
修改Response的Cache-Control時,max-Age不能太大,否則你將在指定的max-Age時間內(nèi)訪問的始終是緩存數(shù)據(jù)(即便是有網(wǎng)的情況下);
實際的開發(fā)過程中,我們在網(wǎng)絡(luò)請求中會添加一些公共參數(shù),對于一些可變的公共參數(shù),在緩存數(shù)據(jù)和訪問緩存數(shù)據(jù)的過程中需要刪除,比如網(wǎng)絡(luò)類型,有網(wǎng)絡(luò)時其值為Wifi或4G等,無網(wǎng)絡(luò)時可能為none, 這時訪問緩存時就會因url不一致導(dǎo)致訪問緩存失敗。
@Override // BaseInterceptor.java public Response intercept(Chain chain) throws IOException { // 添加公共參數(shù) HttpUrl.Builder urlBuilder = chain.request().url().newBuilder() .addQueryParameter("a", "a") .addQueryParameter("b", "b"); Request.Builder requestBuilder = chain.request().newBuilder(); if (NetworkUtil.isConnected(mContext)) { urlBuilder.addQueryParameter("network", NetworkUtil.getNetwokType(mContext)); } else { // 無網(wǎng)絡(luò)時不添加可變的公共參數(shù) requestBuilder.removeHeader("Pragma") .header("Cache-Control", "only-if-cached, max-stale=" + 30 * 24 * 60 * 60); } Request newRequest = requestBuilder .url(urlBuilder.build()) .build(); return chain.proceed(newRequest); } @Override // HttpCacheInterceptor.java public Response intercept(Chain chain) throws IOException { Response response = chain.proceed(chain.request()); HttpUrl newUrl = chain.request().url().newBuilder() .removeAllQueryParameters("network") .build(); // 緩存數(shù)據(jù)前刪除可變的公共參數(shù) Request newRequest = chain.request().newBuilder() .url(newUrl) .build(); return response.newBuilder() .request(newRequest) .removeHeader("Pragma") .header("Cache-Control", "public, max-age=" + 1) .build(); }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/61922.html
摘要:若攔截事件返回為,表示攔截,事件不會向下層的或者傳遞,表示不攔截,繼續(xù)分發(fā)事件。五注冊反注冊未成對使用引起的內(nèi)存泄漏。七集合對象沒有及時清理引起的內(nèi)存泄漏。 原文鏈接:https://blog.csdn.net/wen_hah... 版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接! 前言 金三銀四到來了,找工作的好時候到了,小伙伴們是不是都在忙著找工作呢,小弟前一陣也是忙著在找工作,...
閱讀 2327·2021-11-22 12:01
閱讀 2003·2021-11-12 10:34
閱讀 4531·2021-09-22 15:47
閱讀 2844·2019-08-30 15:56
閱讀 2876·2019-08-30 15:53
閱讀 2416·2019-08-30 13:53
閱讀 3389·2019-08-29 15:35
閱讀 3133·2019-08-29 12:27