其实OkHttp是自带缓存的

  1. 通过配置cache目录后OkHttp就会缓存所有get请求,为啥只缓存get请求,只能说人家设计的很规范;如果get、post随便设置,那你就自行解决吧,大不了和后台的干上一架:)
  2. OkHttp缓存完全遵守http协议,所以完全用http协议的缓存来实现缓存策略配置;

OkHttp设置cache目录

1
2
3
4
5
6
7
okhttp3.OkHttpClient.Builder clientBuilder=new okhttp3.OkHttpClient.Builder();
...
int cacheSize = 200 * 1024 * 1024;
File cacheDirectory = new File(mContext.getCacheDir(), "httpcache");
Cache cache = new Cache(cacheDirectory, cacheSize);
OkHttpClient client = clientBuilder.cache(cache).build();
...

设置cache目录后会出现如下的缓存文件:

参考cache图

主要文件.0是保存header内容,.1是body内容,看来只要设置cahce目录就可以出现http缓存文件;

http缓存

http缓存的东西比较多,可以推荐看看HTTP缓存控制小结这篇文章,目前主要通过 Cache-Control 字段来控制,Cache-Control中的max-age字段可以控制客户端在上次请求后的max-age秒内数据是新鲜的,可以直接用,所以我们可以通过控制max-age字段来控制缓存,请注意这是说的Cache-Control字段是服务端响应字段,也就是必需要让server添加Cache-Control header字段控制,但是不是所有的server都配置这个字段,好在okhttp可以在响应中模拟添加Cache-Control字段以达到此目的;

可以通过添加拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class CacheInterceptorOnNet implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
Long maxAge = 30;
return response.newBuilder()
.removeHeader("Cache-Control")
.header("Cache-Control", "public,max-age="+maxAge)
.removeHeader("Pragma")
.build();
}
}

如上就在Response中强制添加Cache-Control 字段并设置max-age为30s,同时删除了其它干扰字段;注意上面的拦截器一定要添加在addNetworkInterceptor接口,不然没有作用;

1
clientBuilder.addNetworkInterceptor(new CacheInterceptorOnNet());

无网缓存

上面的缓存配置是在30秒以内从缓存拿数据,超出30秒缓存就销毁了,但是如果是在断网的情况下我们期望还是能拿到缓存数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CacheForceInterceptorNoNet implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetUtils.isConnectNet(RetrofitCache.getInatance().getContext())){
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
LogUtil.d("get data from cache");
}
Response response = chain.proceed(request);
return response;
}
}

如上配置设置无网时强制从缓存拿数据,注意此拦截器要添加到addInterceptor接口

1
clientBuilder.addInterceptor(new CacheForceInterceptorNoNet());

灵活配置

一般我们的不同接口需要配置不同缓存时间,如何才能方便的灵活配置缓存时间?

目前Retrofit+okhttp的方式最为火爆,Retrofit通过接口+注解的方式使得请求更为方便,所以我想着通过注解来实现缓存配置:

1
2
3
@Cache(time = 20,timeUnit = TimeUnit.MINUTES)
@GET("users")
Observable<HttpResult> test();

这样用起来就很方便了,我把实现的具体方法放置github RetrofitCache,欢迎大家讨论拍砖;