版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/knockheart/article/details/52814022
早先对于服务器数据缓存处理一般是本地SP或者Sqlite;现在网络请求改为Retrofit+OkHttp,OkHttp是有缓存策略的,
今天我们就来说怎么实现Retrofit与OkHttp的缓存实现。
使用缓存的目的
减少服务器负荷,降低延迟提升用户体验。复杂的缓存策略会根据用户当前的网络情况采取不同的缓存策略,比如在2g网络很差的情况下,提高缓存使用的时间;不用的应用、业务需求、接口所需要的缓存策略也会不一样,需要数据的实时性,采用缓存就是无意义!根据实际应用情况,制定自己的缓存策略。
Retrofit+OkHttp的缓存机制
在响应请求之后在 data/data/<包名>/cache 下建立一个response 文件夹,保持缓存数据。
这样我们就可以在请求的时候,如果判断到没有网络,自动读取缓存的数据。
同样这也可以实现,在我们没有网络的情况下,重新打开App可以浏览的之前显示过的内容。
也就是:判断网络,有网络,则从网络获取,并保存到缓存中,无网络,则从缓存中获取。
这样我们就可以在请求的时候,如果判断到没有网络,自动读取缓存的数据。
同样这也可以实现,在我们没有网络的情况下,重新打开App可以浏览的之前显示过的内容。
也就是:判断网络,有网络,则从网络获取,并保存到缓存中,无网络,则从缓存中获取。
实现缓存
1、开启缓存
这一步是设置缓存路径,以及缓存大小
<span style="font-size:14px;">File httpCacheDirectory = new File(context.getExternalCacheDir(), "responses");
//设置缓存 10M
Cache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
client = new OkHttpClient.Builder().cache(cache).build();</span>
2、
设置 OkHttp 拦截器
两个操作都是在 Interceptor 中进行的
- 通过 CacheControl 控制缓存数据
CacheControl.Builder cacheBuilder = new CacheControl.Builder();
cacheBuilder.maxAge(0, TimeUnit.SECONDS);//多次访问一个接口,<span style="white-space:pre"> </span>设置请求缓存时间,超过时间重新请求,否则去缓存
cacheBuilder.maxStale(365,TimeUnit.DAYS);//这个是控制缓存的过时时间
CacheControl cacheControl = cacheBuilder.build();
- 设置拦截器
Request request = chain.request();
if(!StateUtils.isNetworkAvailable(MyApp.mContext)){
request = request.newBuilder()
.cacheControl(cacheControl)
.build();
}
Response originalResponse = chain.proceed(request);
if (StateUtils.isNetworkAvailable(MyApp.mContext)) {
int maxAge = 60; // read from cache
return originalResponse.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public ,max-age=" + maxAge)
.build();
} else {
int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
return originalResponse.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
.maxAge(0,TimeUnit.SECONDS)设置的时间比拦截器长是不起效果
设置比拦截器设置的时间短就会以这个时间为主,我觉得是为了方便控制。
maxStale(365, TimeUnit.DAYS)设置的是过时时间,okthhp缓存分成了两个来考虑,一个是为了请求时直接拿缓存省流量,一个是为了下次进入应用时可以直接拿缓存。
直接上代码
private HttpControl(final Context context) {
cookieStore = new PersistentCookieStore(context);
CookieHandler cookieHandler = new CookieManager(cookieStore,
CookiePolicy.ACCEPT_ALL);
Interceptor interceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
/**
* 未联网获取缓存数据
*/
if (!CheckHasNet.isNetWorkOk(context)) {
//在20秒缓存有效,此处测试用,实际根据需求设置具体缓存有效时间
CacheControl cacheControl = new CacheControl.Builder()
.maxStale(30, TimeUnit.DAYS)
.build();
request = request.newBuilder()
.cacheControl(cacheControl)
.build();
}
return chain.proceed(request);
}
};
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.d("MyTAG", "OkHttp: " + message);
}
});
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
File httpCacheDirectory = new File(context.getExternalCacheDir(), "responses");
//设置缓存 10M
Cache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
//1.创建Retrofit对象
client = new OkHttpClient.Builder().addInterceptor(logging)
.addInterceptor(interceptor)//离线
.addNetworkInterceptor(provideCacheInterceptor())//在线
.cache(cache)
.readTimeout(30000, TimeUnit.MILLISECONDS)
.connectTimeout(30000, TimeUnit.MILLISECONDS)
.cookieJar(new JavaNetCookieJar(cookieHandler))
.build();
retrofit = new Retrofit.Builder().client(client).baseUrl(Constant.BASEURL)// 定义访问的主机地址
.validateEagerly(true)
// .addConverterFactory(GsonConverterFactory.create())//解析方法 Gson
.addConverterFactory(JsonConverterFactory.create())//解析方法
// .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
public static Interceptor provideCacheInterceptor ()
{
return new Interceptor()
{
@Override
public Response intercept (Chain chain) throws IOException
{
Response response = chain.proceed( chain.request() );
// re-write response header to force use of cache
// 正常访问同一请求接口(多次访问同一接口),给30秒缓存,超过时间重新发送请求,否则取缓存数据
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(3, TimeUnit.SECONDS )
.build();
return response.newBuilder()
.header("Cache-Control", cacheControl.toString() )
.build();
}
};
}
public PersistentCookieStore getCookieStore() {
return cookieStore;
}
/**
* 单例模式
*
* @return
*/
public static HttpControl getInstance(Context context) {
if (instance == null) {
synchronized (HttpControl.class) {
if (instance == null) {
instance = new HttpControl(context);
}
}
}
return instance;
}
public OkHttpClient getClient() {
return client;
}
public void setClient(OkHttpClient client) {
this.client = client;
}
public <T> T create(final Class<T> service) {
return retrofit.create(service);
}
注意
1、缓存是在每一次网络请求之后,重新保存的,所以在超过缓存过期时间后,Retrofit会在检查到没缓存之后自动请求网络服务器数据
2、缓存数据也是需要网络下载的,所以在网络不好的情况下,可能不能立即缓存
3、根据自身实际情况,制定缓存策略