网络访问组件六- 让 okhttp 支持 post缓存

参考:

  1. http://www.jianshu.com/p/2710ed1e6b48

整体代码与示例,请参考:

https://github.com/zhaoyubetter/basenet
至此网络模块的封装告一段落;

为什么需要POST缓存

大环境所致,在servlet中,有2个方法,分别是 doGet, doPost,要么在 doGet中,转发到了doPost,要么反之,springMVC,已记不清了。
所以这就造成了,有时候,我们真的需要 为 post添加缓存;

为post请求添加缓存原理

参考图片:
在 application 级别拦截请求,因为:应用拦截器允许短路而不调用 Chain.proceed(),即中止调用
判断如果是 post,如果缓存有效,直接返回response,让后续的拦截器无法执行;
其他缓存的内容,几乎都是 Cache.java那套;少量修改;


网络访问组件六- 让 okhttp 支持 post缓存_第1张图片
图片来自网络

具体实现:

  1. 为 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());
     }
    
  2. 新增类 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 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();
    }
  1. 应用级拦截器: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,请求,一律无效;

你可能感兴趣的:(网络访问组件六- 让 okhttp 支持 post缓存)