网上有许多写OKhttp3缓存的文章,例如:
【Okhttp3结合Retrofit2 实现缓存】https://www.jianshu.com/p/74d2c10c3eba?from=timeline
【使用Retrofit和Okhttp3实现网络缓存】https://www.jianshu.com/p/34f73e571ecb
【okhttp3缓存实践】http://blog.csdn.net/wuhengde/article/details/54927096
这些文章都很不错,但还是有一些小小的瑕疵,这里我参考他们的文章结合自己的实践简单封装了Okhttp3的五种缓存方式供大家参考,如有错误还请不吝赐教。
主要知识点:
- OkHttp的缓存机制通过拦截器(Interceptor)来实现,通过addInterceptor()(应用拦截器)和addNetworkInterceptor()(网络拦截器)来添加;
- Interceptor的访问机制:在有网状态下先访问应用拦截器,再访问网络拦截器;而在无网络状态只访问应用拦截器;
- 缓存实现思路:在网络拦截器中请求网络数据并缓存到本地,而后在应用拦截器中访问本地缓存;
- 缓存类别:缓存可分为在线缓存和离线缓存,在线缓存例如客户端在短时间内多次请求网络数据,这时就可将在线缓存数据返回,但是要注意这个缓存的有效期不要太长了,否则服务端数据改变了不能及时更新;离线缓存例如在无网络状态下想要客户端还能有数据展示,就需要长时间缓存到本地并在有网状态下及时更新缓存数据;
- 在线缓存与离线缓存的异同:两者都是一个缓存文件,只是header设置不同,例如在线缓存header("max-age=30"),而离线缓存header("if-only-cached,max-age=86400"),两者可以通过删除header和设置header进行切换。
public class OkHttpUtil {
public static class CacheStrategy {
/**
* 只读取网络数据
*/
public static final int ONLY_NETWORK = 0x0101;
/**
* 只读取缓存数据
*/
public static final int ONLY_CACHE = 0x0102;
/**
* 有网读取网络数据,无网读取缓存
*/
public static final int IF_NET_NETWORK__IF_NOT_CACHE = 0x0103;
/**
* 在线缓存有效时读取缓存,无效时读取网络数据
*/
public static final int IF_NET_EXCEED_NETWORK_ELSE_CACHE = 0x0104;
/**
* 有网在线缓存有效时读取缓存,无效时读取网络数据;无网直接读取缓存
*/
public static final int IF_NET_EXCEED_NETWORK_ELSE_CACHE__IF_NOT_CACHE = 0x0105;
}
/**
* 创建OkHttp客户端,并实现多种缓存模式
* 拦截器:
* 1.应用拦截器:addInterceptor
* 2.网络拦截器:addNetworkInterceptor
*
* @return
*/
public static OkHttpClient createOkHttpClient(final int cacheStrategy) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new Interceptor() {//应用拦截器——无网状态下的拦截
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response;
switch (cacheStrategy) {
case CacheStrategy.ONLY_CACHE:
response = readCache(chain, request);
break;
case CacheStrategy.IF_NET_NETWORK__IF_NOT_CACHE:
if (NetworkUtil.isNetworkAvailable()) {
response = readNetwork(chain, request);
} else {
response = readCache(chain, request);
}
break;
case CacheStrategy.IF_NET_EXCEED_NETWORK_ELSE_CACHE__IF_NOT_CACHE:
if (NetworkUtil.isNetworkAvailable()) {
response = readNetwork(chain, request);
} else {
response = readCache(chain, request);
}
break;
case CacheStrategy.ONLY_NETWORK:
case CacheStrategy.IF_NET_EXCEED_NETWORK_ELSE_CACHE://当设置了在线缓存,只要缓存没过期OkHttp会自动先获取缓存
default:
response = readNetwork(chain, request);
break;
}
return response;
}
});
if (cacheStrategy != CacheStrategy.ONLY_NETWORK) {
File httpCacheDir = new File(BaseApp.getContext().getExternalCacheDir(), Config.HTTP_CACHE_FILE_NAME);
final Cache cache = new Cache(httpCacheDir, Config.HTTP_CACHE_SIZE);
builder.addNetworkInterceptor(new Interceptor() {//网络拦截器——有网状态下的拦截
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
int maxAge = Config.HTTP_ONLINE_CACHE_TIME;//在线缓存时间
return originalResponse.newBuilder()
.removeHeader("Pragma")//清除头信息
.removeHeader("Cache-Control")
.body(new progressResponse(originalResponse))//获取进度
.header("Cache-Control", "public,max-age=" + maxAge)//若是0则为不设置缓存
.build();
}
}).cache(cache);//设置缓存;
}
builder.connectTimeout(Config.REQUEST_TIMEOUT, TimeUnit.SECONDS)//设置10s超时时间
.retryOnConnectionFailure(true);//连接失败后尝试再次连接
return builder.build();
}
private static Response readNetwork(Interceptor.Chain chain, Request request) throws IOException {
return chain.proceed(request);
}
private static Response readCache(Interceptor.Chain chain, Request request) throws IOException {
Response response;
CacheControl cacheControl = new CacheControl.Builder()
.onlyIfCached()
.maxStale(Config.HTTP_OFFLINE_CACHE_TIME, TimeUnit.DAYS)
.build();
response = chain.proceed(request.newBuilder()
.cacheControl(cacheControl)
.build());
return response;
}
}
后记:如有不同见解或疑惑,欢迎留言,如果觉得不错可以来个赞!点个赞!
网友评论