成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

OkHttp緩存使用指南

lemanli / 1625人閱讀

摘要:如表示可接受過期小時內(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.
    List interceptors = 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

相關(guān)文章

  • 2019最新Android面試題

    摘要:若攔截事件返回為,表示攔截,事件不會向下層的或者傳遞,表示不攔截,繼續(xù)分發(fā)事件。五注冊反注冊未成對使用引起的內(nèi)存泄漏。七集合對象沒有及時清理引起的內(nèi)存泄漏。 原文鏈接:https://blog.csdn.net/wen_hah... 版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接! 前言 金三銀四到來了,找工作的好時候到了,小伙伴們是不是都在忙著找工作呢,小弟前一陣也是忙著在找工作,...

    plus2047 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<