使用Retrofit和Okhttp實現網絡緩存。無網讀緩存,有網根據過期時間重新請求
使用Retrofit和Okhttp實現網絡緩存
本文使用 Retrofit2.0.0-beta2、Okhttp 2.6.0(Okhttp3.0之后api寫法有變化)
-
配置Okhttp的Cache
-
配置請求頭中的cache-control或者統一處理所有請求的請求頭
-
云端配合設置響應頭或者自己寫攔截器修改響應頭中cache-control
最后實現的效果是:有網的時候根據你每個接口設置的需要緩存的時間(1分鐘、5分鐘等)進行緩存,過了時間重新請求;沒網的時候讀緩存。
在這里插一句為什么要做緩存,或者說有什么好處?
減少服務器負荷,降低延遲提升用戶體驗。復雜的緩存策略會根據用戶當前的網絡情況采取不同的緩存策略,比如在2g網絡很差的情況下,提高緩存使用的時間;不用的應用、業務需求、接口所需要的緩存策略也會不一樣,有的要保證數據的實時性,所以不能有緩存,有的你可以緩存5分鐘,等等。你要根據具體情況所需數據的時效性情況給出不同的方案。當然你也可以全部都一樣的緩存策略,看你自己。
1.配置okhttp中的Cache
OkHttpClient okHttpClient = new OkHttpClient();
File cacheFile = new File(context.getCacheDir(), "[緩存目錄]");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
okHttpClient.setCache(cache);
2.配置請求頭中的cache-control
在Retrofit中,我們可以通過@Headers來配置,如:
@Headers("Cache-Control: public, max-age=3600)
@GET("merchants/{shopId}/icon")
Observable<ShopIconEntity> getShopIcon(@Path("shopId") long shopId);
沒有設置的可以即為有網的時候不進行緩存。
或者你所有接口在有網的時候都不需要緩存或者都需要緩存且時間一樣,那么也不用配置每個接口的@Headers的Cache-Control了。
3.云端配合設置響應頭或者自己寫攔截器修改響應頭response中cache-control
到這一步緩存就已經待在你的緩存目錄了。
如果云端有處里cache的話,就已經可以了。
但是很可能云端沒有處理,所以返回的響應頭中cache-control是no-cache,這時候你還是無法做緩存,大家可以用okhttp的寫日志攔截器查看響應頭的內容。
如果云端現在不方便處理的話,你也可以自己搞定緩存的,那就是寫攔截器修改響應頭中的cache-control。我把請求頭中的cache-control讀出來然后設置到了響應頭中。
設置攔截器:
REWRITE_CACHE_CONTROL_INTERCEPTOR攔截器需要同時設置networkInterceptors和interceptors(OKHTTP3.0配置是否有效待我測試)
okHttpClient.interceptors().add(LoggingInterceptor);
okHttpClient.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR);
okHttpClient.interceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR);
攔截器如下:云端響應頭攔截器,用來配置緩存策略
/**
- 云端響應頭攔截器,用來配置緩存策略
- Dangerous interceptor that rewrites the server's cache-control header.
*/
private final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = chain -> {
Request request = chain.request();
if(!NetUtils.hasNetwork(context)){
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
Logger.t(TAG).w("no network");
}
Response originalResponse = chain.proceed(request);
if(NetUtils.hasNetwork(context)){
//有網的時候讀接口上的@Headers里的配置,你可以在這里進行統一的設置
String cacheControl = request.cacheControl().toString();
return originalResponse.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
}else{
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=2419200")
.removeHeader("Pragma")
.build();
}
};</code></pre> 最后日志攔截器也貼上來吧
private final Interceptor LoggingInterceptor = chain -> {
Request request = chain.request();
long t1 = System.nanoTime();
Logger.t(TAG).i(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
Logger.t(TAG).i(String.format("Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
};
以下測試Cache-Control的配置在請求頭和響應頭中都有且一樣。
max-stale在請求頭設置有效,在響應頭設置無效。
max-stale和max-age同時設置的時候,緩存失效的時間按最長的算。
關于max-age和max-stale我這里做了一個測試:
測試結果:
我在請求頭中設置了:Cache-Control: public, max-age=60,max-stale=120,響應頭的Cache-Control和請求頭一樣。
-
在第一次請求數據到一分鐘之內,響應頭有:Cache-Control: public, max-age=60,max-stale=120
-
在1分鐘到3分鐘在之間,響應頭有:Cache-Control: public, max-age=60,max-stale=120
Warning: 110 HttpURLConnection "Response is stale"
可以發現多了一個Warning。
-
三分鐘的時候:重新請求了數據,如此循環,如果到了重新請求的節點此時沒有網,則請求失敗。
另外關于緩存有一個rxcache也可以試試。
來自:http://www.jianshu.com/p/9c3b4ea108a7