Retrofit2.0+Okhttp不依賴服務端的數據緩存

隨着Retrofit在項目中的使用,替換的以前使用的網絡框架,相關的緩存機制也要進行替換,網絡上大部分的Retrofit+okhttp緩存資料都是進行鍼對所有url的而且需要服務端的配合。有些時候是先有服務然後app去調用這些服務,所以這個時候沒有服務端的配合我們在前端實現緩存比較空難但並不是很可以。(舉個列子,有一個原來的服務更本不支持cache,但是我們在app中需要緩存這個服務的數據,這應該是前一段時間替換網絡庫時最後遇到的問題)。

爲什麼要做緩存處理?

客觀回答:

減少服務器負荷,降低延遲提升用戶體驗。複雜的緩存策略會根據用戶當前的網絡情況採取不同的緩存策略,比如在2g網絡很差的情況下,提高緩存使用的時間;不用的應用、業務需求、接口所需要的緩存策略也會不一樣,有的要保證數據的實時性,所以不能有緩存,有的你可以緩存5分鐘,等等。

自己回答:

在絕大部分的項目中我們前端開發人員只是考慮用戶的流量,用戶在產品性能上的體驗。所以有時候服務端和前端沒有依賴,即服務不支持緩存那麼前端又需要緩存那麼我們應該怎麼做?普通的緩存模式已經很難適應這種需求了,下面將的就是利用Retrofit2.0+OkHttp3.0的緩存原理去實現我們的需求。

Retrofit+OkHttp的緩存機制:

  • 在 data/data/<包名>/cache 下建立一個用來進行數據存儲的文件夾,保持緩存數據。
  • 這樣我們就可以在請求的時候,根據業務邏輯,請求網絡數據或者讀取緩存的數據。

緩存使用情況:

  • 一般情況下無網絡,數據從緩存中讀取;
  • 有網絡則根據請求頭,判斷是請求網絡還是讀取緩存。

說到緩存,不是很瞭解的Http緩存的同學亦可以看一下瀏覽器 HTTP 協議緩存機制詳解 這篇文章講的很詳細。

Cache控制:

該部分對理解怎麼緩存很重要~!

1、用在request中的cache控制頭

Pragma: no-cache :兼容早起HTTP協議版本 如1.0+
Cache-Control: no-cache ,表示不希望得到一個緩存內容。只是希望,cache設備可能忽略。
Cache-Control: no-store,表示client與server之間的設備不能緩存響應內容,並應該刪除已有緩存。
Cache-Control: only-if-cached,表示只接受是被緩存的內容

2、用在response中控制cache的頭

Cache-Control: max-age=3600,用相對於接收到的時間開始可緩存多久
Cache-Control: s-maxage=3600,與上面類似,只是s-maxage一般用在cache服務器上,並只對public緩存有效
Expires: Fri, 05 Jul 2002, 05:00:00 GMT基於GMT的時間,絕對時間,但該頭容易受到本地錯誤時間影響
Cache-Control: must-revalidate 該頭表示內容可以被緩存但每次必須詢問是否有更新。

HTTP-請求、響應、緩存

代碼實現:

看到這裏應該對緩存有一定的瞭解了,那麼現在來看看怎麼利用Retrofit2.0+Okhttp緩存的實現。

創建緩存文件,並對okhttp進行設置

public class RetrofitApiFactory{
    private static OkHttpClient okHttpClient = null ;
    private static File cacheFile = new File(ImageUtils.getAppCacheDir(), "xxxCache");
    private static Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
    /******中間代碼省略*****/
    public static void initOkHttpClient(){
        okHttpClient =
                new OkHttpClient.Builder()
                        .cache(cache)
                        .readTimeout(TIME_OUT, TimeUnit.SECONDS)
                        .writeTimeout(TIME_OUT, TimeUnit.SECONDS)
                        .addInterceptor(new JobInterceptor("Interceptor"))  
                        //only 有網情況下,一分鐘內每次請求都會重新請求,不會走緩存
                        .addNetworkInterceptor(new JobInterceptor("NetworkInterceptor"))    
                        //only 如果超過1分鐘,離線請求不成功
                        .build();
        clearCacheMap() ;
    }
}

控制Cache中最最最主要的部分:

public class JobInterceptor implements Interceptor {
    /******中間代碼省略*****/
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request() ;
        if (!TextUtils.isEmpty(getCookie())) {
            if (!NetworkDetection.getIsConnected()) {
                try {
                    request = request
                            .newBuilder()
                            .addHeader("Cookie", getCookie())
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    request = request
                            .newBuilder()
                            .addHeader("Cookie", getCookie())
                            .build();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        Response response = null;
        try {
            response = chain.proceed(request);
            if(!response.isSuccessful()){
                RetrofitApiFactory.initOkHttpClient();
            } else {//如果請求體有緩存數據的需要那麼對響應體進行緩存
                int maxAge = request.cacheControl().maxAgeSeconds();
                if (request.cacheControl().isPublic() && maxAge > 1) {
                    response = response.newBuilder()
                            .removeHeader("Pragma")//清楚響應體對Cache有影響的信息
                            .removeHeader("Cache-Control")//清楚響應體對Cache有影響的信息
                            .header("Cache-Control", "public, max-age=" + maxAge)
                            .build();
                }
            }
        } catch (IOException e) {
            RetrofitApiFactory.initOkHttpClient();
            throw new IOException(e) ;
        }

        return response;
    }
}

接口使用:

@Headers("Cache-Control: public, max-age=86400")
@GET
Call<ResponseBody> getCacheData(@Url String url);

解析:

上面的絕大部分內容大家都在類似的文章上看到的。這裏主要講一下幾點:

一、我們所用的接口服務不支持緩存,所以我不能只修改頭信息而讓服務端返回的response響應體去實現數據本地緩存。當然在沒有網絡的情況下我們可以嘗試去讀取緩存。

二、因爲服務端沒有提供response響應體的緩存,所以我們清除response響應體的Pragma、Cache-Control信息,然後根據自己設定的request請求體中的Cache信息去修改response響應體的Cache信息從而達到數據可以緩存。

三、在開發的過程中遇到如果一個接口在某次請求返回404,那麼以後的結果總是請求失敗的404頁面。所以在請求失敗的時候需要初始化OkHttpClient實例。

如有問題,歡迎溝通指正~!~!
參考文章Retrofit-Cache

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章