网络访问组件五思考(引入缓存)

参考:

  1. http://www.jianshu.com/p/2710ed1e6b48
  2. 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的缓存配置:

  1. 设置缓存路径,与缓存大小就可使用了:
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 缓存,下一篇,我们引入;

你可能感兴趣的:(网络访问组件五思考(引入缓存))