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

参考:

  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 缓存,下一篇,我们引入;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容