参考:
整体代码与示例,请参考:
https://github.com/zhaoyubetter/basenet
至此网络模块的封装告一段落;
为什么需要POST缓存
大环境所致,在servlet中,有2个方法,分别是 doGet, doPost,要么在 doGet中,转发到了doPost,要么反之,springMVC,已记不清了。
所以这就造成了,有时候,我们真的需要 为 post添加缓存;
为post请求添加缓存原理
参考图片:
在 application 级别拦截请求,因为:应用拦截器允许短路而不调用 Chain.proceed(),即中止调用
判断如果是 post,如果缓存有效,直接返回response,让后续的拦截器无法执行;
其他缓存的内容,几乎都是 Cache.java那套;少量修改;
具体实现:
-
为 OkhttpClient添加应用拦截器:
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()); } // 是否允许POST 形式缓存设置 if (NetConfig.getInstance().isEnablePostCache()) { builder.addInterceptor(new PostCacheInterceptor()); }
新增类 NetPostCache.java, 此类,就是 copy 了 Cache类,然后修改
其put方法,让其支持post,与 获取key方法,如下:
public static String key(Request request) {
String cUrl = request.url().toString();
if (request.body() != null) {
Buffer buffer = new Buffer();
try {
// 避免post重复,这里采用value来凭借,因key不好获取
// 如果有上传下载文件,此处为 ProgressRequestBody
if (request.body() instanceof MultipartBody) {
final List<MultipartBody.Part> parts = ((MultipartBody) request.body()).parts();
/**
* 接受字符串格式的参数,其他忽略
* @see lib.basenet.okhttp.OkHttpRequest#getRequestBody mParams
*/
for (MultipartBody.Part p : parts) {
if (null == p.body().contentType()) {
p.body().writeTo(buffer);
}
}
}
String params = buffer.readString(Charset.forName("UTF-8")); //获取请求参数
cUrl += params;
} catch (IOException e) {
e.printStackTrace();
} finally {
Util.closeQuietly(buffer);
}
}
return ByteString.encodeUtf8(cUrl).md5().hex();
}
- 应用级拦截器:PostCacheInterceptor.java 代码片段
/**
* post缓存,从应用拦截器层,拦截
* Created by zhaoyu on 2017/4/26.
*/
public class PostCacheInterceptor implements Interceptor {
/**
* 缓存Post请求
*/
final NetPostCache mPostCache;
public PostCacheInterceptor() {
File cacheDir = new File(NetConfig.getInstance().getCacheDir() + "/post");
mPostCache = new NetPostCache(cacheDir, NetConfig.getInstance().getCacheSize());
}
@Override
public Response intercept(Chain chain) throws IOException {
final Request request = chain.request();
// 如果是post请求,并且设置了缓存,则在这里进行拦截,如果缓存有效,后续的拦截器将不执行
if ("POST".equalsIgnoreCase(request.method()) && (null != request.cacheControl() && !request.cacheControl().noStore())) {
// 强制刷新,删除旧有post缓存
checkForceRefresh(request);
// 以下代码逻辑来家:okhttp3 源码中的 CacheInterceptor.java,模拟其运行
Response cacheCandidate = mPostCache != null ? mPostCache.get(request) : null;
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (mPostCache != null) {
mPostCache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The mPostCache candidate wasn't applicable. Close it.
}
// 有则从缓存中返回,直接跳过后面的拦截器,不访问网络了
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
// 执行网络请求,并包装一下
networkResponse = chain.proceed(request);
} finally {
// If we're crashing on I/O or otherwise, don't leak the mPostCache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
if (HttpHeaders.hasBody(networkResponse)) {
CacheRequest cacheRequest = mPostCache.put(networkResponse);
networkResponse = cacheWritingResponse(cacheRequest, networkResponse);
}
// 当 onSuccess调用时,会写入缓存
return networkResponse;
}
return chain.proceed(request);
}
注意事项:
post 缓存时,因 唯一key,不好定位,只支持 请求参数为 键值对(key-value)form 表单形式,对于上传文件,下载文件形式的post,请求,一律无效;