参考:
- http://www.jianshu.com/p/2710ed1e6b48
-
http://www.cnblogs.com/whoislcj/p/5537640.html
好久不曾更新了。这回来个好用的;_
代码地址(支持 post 形式缓存):
https://github.com/zhaoyubetter/basenet
为什么需要缓存
有些基础数据,变动不是很频繁,我们可以将数据缓存在本地,下次请求时,就直接从本地读了,可以节约用户流量与加快响应速度;
okhttp的缓存机制
但是。okhttp默认只支持 GET 形式,而且,它的 cache类还是个final类,还不能继承,此篇,我们考虑 引入 GET 形式的缓存,从okhttp上的源码,得出,她就是使用 LruDiskCache来实现缓存的;
// okhttps Cache.java 代码
CacheRequest put(Response response) {
String requestMethod = response.request().method();
if (HttpMethod.invalidatesCache(response.request().method())) {
try {
remove(response.request());
} catch (IOException ignored) {
// The cache cannot be written.
}
return null;
}
// 其他方式直接拒绝
if (!requestMethod.equals("GET")) {
// Don't cache non-GET responses. We're technically allowed to cache
// HEAD requests and some POST requests, but the complexity of doing
// so is high and the benefit is low.
return null;
}
okhttp3严格按规范来,来定义使用缓存,基于就是 http协议中的 头相关信息,具体请参考其他博客:
http://www.cnblogs.com/whoislcj/p/5537640.html
规范化的 Okhttp的缓存配置:
- 设置缓存路径,与缓存大小就可使用了:
static {
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(10 * 1000, TimeUnit.MILLISECONDS);
builder.readTimeout(10 * 1000, TimeUnit.MILLISECONDS);
builder.writeTimeout(10 * 1000, TimeUnit.MILLISECONDS);
/* ==设置拦截器== */
// 1.设置缓存
File cacheDir = new File(NetConfig.getInstance().getCacheDir());
// 2.GET 形式缓存设置
Cache cache = new Cache(cacheDir, NetConfig.getInstance().getCacheSize());
// 日志拦截
if (NetConfig.getInstance().isDebug()) {
builder.addInterceptor(new LoggerInterceptor());
}
sOkHttpClient = builder.build();
}
自定义的缓存设置
app开发者,可以要求服务端,严格按照 协议规范 来设计,缓存规范,如:加入 cache-control 等消息头,这样 客户端只需要简单配置,就可以使用缓存了;
但问题是,一般服务端,遵循规范的实在太少,这个工作,还交给前端开发了,这个时候,需要开发者,酌情来定义缓存策略了;
以我们公司的情况为例,我的配置如下:
AbsRequest 抽象类加入缓存控制
用户请求时,自己来设置是否缓存,缓存时间,如下代码:
public abstract class AbsRequest implements IRequest {
public static final String CHAR_SET = "UTF-8";
.....
.....
/**
* 缓存时间,秒为单位
*/
protected int mCacheTime;
/**
* 是否强制刷新,不管有没有缓存,都从网络获取
*/
protected boolean mIsForceRefresh = false;
在okHttp 构建request 过程中,修改如下:
/**
* 设置缓存
*/
private void setCache(final Request.Builder tBuilder) {
if (mIsForceRefresh) {
// 1.情形一:强制刷新,从网络上获取(强制刷新时,如果有 mCacheTime,也马上缓存)
tBuilder.cacheControl(new CacheControl.Builder().maxAge(mCacheTime, TimeUnit.SECONDS).noCache().build()).build();
} else if (mCacheTime > 0) {
// 2.情形二:设置缓存时间 注意:当重复请求时,缓存的起点时间是第一次请求成功的时间
tBuilder.cacheControl(new CacheControl.Builder().maxAge(mCacheTime, TimeUnit.SECONDS).build()).build();
} else {
// 3.情形三(默认):不缓存,也不存储 (如:用户登录接口、获取验证码等)
tBuilder.cacheControl(new CacheControl.Builder().noCache().noStore().build()).build();
}
}
为 okhttpclient 设置缓存拦截器:
static {
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(NetConfig.getInstance().getTimeOut(), TimeUnit.MILLISECONDS);
builder.readTimeout(NetConfig.getInstance().getTimeOut(), TimeUnit.MILLISECONDS);
builder.writeTimeout(NetConfig.getInstance().getTimeOut(), TimeUnit.MILLISECONDS);
/* ==设置拦截器== */
// 设置缓存
File cacheDir = new File(NetConfig.getInstance().getCacheDir());
// GET 形式缓存设置
Cache cache = new Cache(cacheDir, NetConfig.getInstance().getCacheSize());
// ===== 设置缓存拦截器
builder.cache(cache).addNetworkInterceptor(new NetCacheInterceptor()); // 设置缓存拦截器
// 日志拦截
if (NetConfig.getInstance().isDebug()) {
builder.addInterceptor(new LoggerInterceptor());
}
sOkHttpClient = builder.build();
}
最后看看我们的拦截器:
package lib.basenet.okhttp.cache;
import java.io.IOException;
import lib.basenet.config.NetConfig;
import lib.basenet.utils.NetworkUtils;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* 缓存拦截
*/
public class NetCacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 从request获取是否能存储缓存
if (null != request.cacheControl() && !request.cacheControl().noStore()) {
// 是否有网络
boolean hasNet = NetworkUtils.hasNet(NetConfig.getInstance().getApplication());
// 原始响应
final Response originResponse;
if (!hasNet) {
// 无网络,强制从缓存中获取
request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
originResponse = chain.proceed(request);
return originResponse;
} else {
// 有网络,设置缓存时间 (通过下面一行,可拿到网络上的返回数据)
originResponse = chain.proceed(request);
// 再包装一下originResponse,方便后面的 CacheInterceptor、PostCacheInterceptor 来做缓存处理
return getWrapperResponse(request, originResponse);
}
} // end 能存储缓存
return chain.proceed(request);
}
/**
* 包装一下originResponse,方便后面的 CacheInterceptor 来做缓存处理
*
* @param request
* @param originResponse
* @return
*/
private Response getWrapperResponse(Request request, Response originResponse) {
// 这个时间来自 上面的 setCache 方法
int maxAge = request.cacheControl().maxAgeSeconds();
return originResponse.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-control")
.header("Cache-control", "public, max-age=" + maxAge)
.build();
}
}
通过上面的设置,我们支持客户端,自己来设置缓存时间,仅限于 GET请求;
POST 缓存,下一篇,我们引入;