okhttp3缓存设置及拦截器的使用

缓存:

服务器支持缓存

如果服务器支持缓存,请求返回的Response会带有这样的Header:Cache-Control, max-age=xxx,这种情况下我们只需要手动给okhttp设置缓存就可以让okhttp自动帮你缓存了。这里的max-age的值代表了缓存在你本地存放的时间。

OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClient newClient = okHttpClient.newBuilder()
               .cache(new Cache(mContext.getCacheDir(), 10240*1024))
               .connectTimeout(20, TimeUnit.SECONDS)
               .readTimeout(20, TimeUnit.SECONDS)
               .build();

最简单的使用

提供一个判断手机SD卡 是否存在,是否可读

较优秀的程序都会专门写一个方法来获取缓存地址,如下所示:
public String getDiskCacheDir(Context context) {  
    String cachePath = null;  
//Environment.getExtemalStorageState() 获取SDcard的状态
//Environment.MEDIA_MOUNTED 手机装有SDCard,并且可以进行读写
    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())  
            || !Environment.isExternalStorageRemovable()) {  
        cachePath = context.getExternalCacheDir().getPath();  
    } else {  
        cachePath = context.getCacheDir().getPath();  
    }  
    return cachePath;  
}  

 如果有SD卡可以直接设置路径,但是最好用上面的方法判断一下这样比较好 

/**
     * 有网时候的缓存
     */
    final Interceptor NetCacheInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);
            int onlineCacheTime = 30;//在线的时候的缓存过期时间,如果想要不缓存,直接时间设置为0
            return response.newBuilder()
                    .header("Cache-Control", "public, max-age="+onlineCacheTime)
                    .removeHeader("Pragma")
                    .build();
        }
    };
    /**
     * 没有网时候的缓存
     */
    final Interceptor OfflineCacheInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (!SystemTool.checkNet(AppContext.context)) {
                int offlineCacheTime = 60;//离线的时候的缓存的过期时间
                request = request.newBuilder()
//                        .cacheControl(new CacheControl
//                                .Builder()
//                                .maxStale(60,TimeUnit.SECONDS)
//                                .onlyIfCached()
//                                .build()
//                        ) 两种方式结果是一样的,写法不同
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + offlineCacheTime)
                        .build();
            }
            return chain.proceed(request);
        }
    };
//setup cache
    File httpCacheDirectory = new File(AppContext.context.getCacheDir(), "okhttpCache");
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache cache = new Cache(httpCacheDirectory, cacheSize);
    OkHttpClient client = new OkHttpClient.Builder()
            .addNetworkInterceptor(NetCacheInterceptor)
            .addInterceptor(OfflineCacheInterceptor)
            .cache(cache)
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .build();

对于这段代码疑惑点:

1、max-age是啥,maxStale是啥,他们的区别是啥?

2、为什么没有网络的情况下,request要cacheControl.FORCE_CACHE

3、为什么又要对response设置header的cache-control,到底request的设置跟response的设置有什么区别?

4、addNetInterceptor和addInterceptor有什么区别?

解答:

1、max-age是啥,maxStale是啥,他们的区别是啥?

maxAge和maxStale的区别在于:

maxAge:没有超出maxAge,不管怎么样都是返回缓存数据,超过了maxAge,发起新的请求获取数据更新,请求失败返回缓存数据。

maxStale:没有超过maxStale,不管怎么样都返回缓存数据,超过了maxStale,发起请求获取更新数据,请求失败返回失败

2、为什么没有网络的情况下,request要cacheControl.FORCE_CACHE

public static final CacheControl FORCE_CACHE = new Builder()

.onlyIfCached()

.maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)

.build();

可以看到FORCE_CACHE是设置了maxStale的最大时间为interger的最大时间,所以,意思就是无论如何,都不会超过这个时间,所以就是一直(强制)拿缓存,也是想要实现缓存的正确逻辑

一般控制缓存有两种方式:

1、在request里面去设置cacheControl()策略

2、在header里面去添加cache-control

后面也会看到,在request里面设置header的cache-control和调用cacheControl方法来设置其实是一样的,我们就是通过在request里面来控制无网缓存的maxStale过期时间的

3、为什么又要对response设置header的cache-control,到底request的设置跟response的设置有什么区别?

其实我到现在都还没有搞清楚,为啥那些人要这样写,只是后面我在测试的过程中推断出来,有网的时候和无网的时候对于interceptor的调用是不同的,产生的结果也是不同的。比如request设置的时候就对无网缓存及其时间控制有效,response就不行

4、addNetInterceptor和addInterceptor有什么区别?

addNetInterceptor是添加网络拦截器,addInterceptor是添加应用拦截器,如果看到okhttp的流程分析的知道:应用拦截器是在网络拦截器前执行的。

如果我使用的是addNetInterceptor:

1、有网的情况下,可以在期限内拿到缓存,而没有去请求接口(通过测试数据库的数据改动来判断的)

2、没有网的情况下,直接就ConnectException了,根本不会走到interceptor里面去了。(网上很多人都提出了这样的问题)

如果我使用的是addInterceptor:

1、有网的情况下,明明设置的是60秒,但是每次都没有去拿缓存而都是请求的接口。(通过测试数据库的数据改动来判断的)

2、没有网的情况下,可以拿到缓存数据(猜想:可能是因为应用拦截器在网络拦截器前执行,没有网的情况下,本身就执行不到网络拦截器里面去),但是缓存过期时间是“永久”,因为FORCE_CACHE里面已经设置为了integer的最大值,21亿秒左右,堪称永久

但是,依旧没有办法控制无网时候的缓存过期时间

面对这些个问题,我也是很无奈。只得不停地尝试,不停地摸索。于是就在尝试的过程中发现了它的一些规则,于是最终写出了一个自认为“万全”的方法。

  • 既然想要有网的情况下拿缓存,那么就需要addNetInterceptor,如果需要无网的情况下拿缓存,就需要addInterceptor,所以不如直接做两个interceptor吧!
  • 另外,如果想要控制有网的时候不去读取缓存,可以直接通过在response里设置maxAge=0来实现。
  • 这里通过大量实验发现,只有在request去设置其maxStale才能控制无网时候的缓存时间,在response里面去控制是不行的!

 

出于安全性的考虑,在Android中我们推荐使用Context.getCacheDir()来作为缓存的存放路径,另外还需要指定缓存的大小就可以创建一个缓存了。如下所示:

注: 在Activity中有 getFileDir() 和 getCacheDir(); 方法可以获得当前的手机自带的存储空间中的当前包文件的路径 
getFileDir() ----- /data/data/cn.xxx.xxx(当前包)/files 
getCacheDir() ----- /data/data/cn.xxx.xxx(当前包)/cache 

OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClient newClient = okHttpClient.newBuilder()
                //Android推荐使用getCacheDir()/getFileDir() 来获取手机缓存路径
               .cache(new Cache(mContext.getCacheDir(), 10240*1024))
               .connectTimeout(20, TimeUnit.SECONDS)
               .readTimeout(20, TimeUnit.SECONDS)
               .build();

服务器不支持缓存

如果服务器不支持缓存就可能没有指定这个头部,或者指定的值是如no-store等,但是我们还想在本地使用缓存的话要怎么办呢?这种情况下我们就需要使用Interceptor来重写Respose的头部信息,从而让okhttp支持缓存。
如下所示,我们重写的Response的Cache-Control字段

public class CacheInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        Response response1 = response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                //cache for 30 days
                .header("Cache-Control", "max-age=" + 3600 * 24 * 30)
                .build();
        return response1;
    }
}

然后将该Intercepter作为一个NetworkInterceptor加入到okhttpClient中:

OkHttpClient okHttpClient = new OkHttpClient();

OkHttpClient newClient = okHttpClient.newBuilder()
        .addNetworkInterceptor(new CacheInterceptor())
        .cache(cache)
        .connectTimeout(20, TimeUnit.SECONDS)
        .readTimeout(20, TimeUnit.SECONDS)
        .build();

这样服务器不支持缓存的情况下使用缓存。

Cache-Control 为单向指令,即请求中存在的指令,并不意味着响应中存在同样的指令

客户端对请求的处理(请求头设置的Cache-Control):

Web浏览器都有Refresh(刷新)和Reload(重载)按钮,可以强制对浏览器或者代理缓存,进行刷新

Refresh的特性取决于特定的浏览器、文档以及拦截缓存的配置。可以通过下面的指令来强化客户端的缓存配置

Pragma: no-cache

跟Cache-Control: no-cache相同,Pragma: no-cache兼容http 1.0 ,Cache-Control: no-cache是http 1.1提供的。因此,Pragma: no-cache可以应用到http 1.0 和http 1.1,而Cache-Control: no-cache只能应用于http 1.1.

请求中Cache-Control指令
指令目的
Cache-Control:max-stale=<s>缓存可随意提供过期文件,如果指定了参数<s> 在这段时间内,文档不能过期
Cache-Control:max-age=<s>缓存无法返回缓存时间大于<s>秒的文件,这条指令使得缓存更加严格
Cache-Control:no-cache除非资源进行了再验证,否则客户端不会接受已经缓存的资源
Cache-Control:no-store缓存尽快从存储器中删除资源痕迹
Cache-Control:only-if-cached当缓存中有副本文件存在才,客户端才会获取副本

 

 

 

 

 

 

 

服务器对Cache-Control的配置:

响应的Cache-Control指令
指令说明
public所有内容都被缓存
private仅客户端缓存代理服务器不缓存
no-cache必须先与代理服务器确认是否更改,然后在在决定使用缓存还是请求
no-store所有内容都不会被缓存
must-revalidation/proxy-revalidation如果缓存内容失效,请求必须发送服务器/代理进行验证
max-age=<s>缓存内容在s秒后失效,仅HTTP1.1可用


对于上述配置浏览器的响应为:

 

浏览器对于Cache-Control的响应
指令打开新窗口原窗口单击Enter按钮刷新点击返回按钮
public缓存页面缓存页面重新请求页面缓存页面
private重新请求第一次请求,随后缓存重新请求页面缓存页面
no-cache/no-store重新请求重新请求重新请求缓存页面
must-revalidation/proxy-revalidation浏览器重新请求第一次请求,随后缓存重新请求缓存页面
max-age=xxx在XX秒后重新请求在XX秒后重新请求重新请求在xx秒后重新请求
     noCache();//不使用缓存,全部走网络
     noStore();//不使用缓存,也不存储缓存
     onlyIfCached();//只使用缓存
     noTransform();//禁止转码
     maxAge(10, TimeUnit.MILLISECONDS);//指示客户机可以接收生存期不大于指定时间的响应。
     maxStale(10, TimeUnit.SECONDS);//指示客户机可以接收超出超时期间的响应消息
     minFresh(10, TimeUnit.SECONDS);//指示客户机可以接收响应时间小于当前时间加上指定时间的响应。

拦截器的选择
1)应用拦截器
不需要担心中间过程的响应,如重定向和重试.
总是只调用一次,即使HTTP响应是从缓存中获取.
观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.
允许短路而不调用 Chain.proceed(),即中止调用.
允许重试,使 Chain.proceed()调用多次.
2)网络拦截器
能够操作中间过程的响应,如重定向和重试.
当网络短路而返回缓存响应时不被调用.
只观察在网络上传输的数据.
携带请求来访问连接.

无论有无网络我们都先获取缓存的数据。

1.先要创建拦截器。

/**
     * 一、无论有无网路都添加缓存。
     * 目前的情况是我们这个要addNetworkInterceptor
     * 这样才有效。经过本人测试(chan)测试有效.
     * 60S后如果没有网络将获取不到数据,显示连接失败
     */
    static Interceptor netInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);
          /*String cacheControl = request.header("Cache-Control");
            if (TextUtils.isEmpty(cacheControl)) {
                cacheControl = "public, max-age=60";
            }*/
            int maxAge = 60;
            return response.newBuilder()
                    .removeHeader("Pragma")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, max-age=" + maxAge)
                    .build();
        }
    };

2.这样的话就写好了拦截器,然后哦就是把拦截器设置到okhttp里面

 File cacheFile = new File(BaseApp.getInstance().getCacheDir(), "caheData");
        //设置缓存大小
        Cache cache = new Cache(cacheFile, DEFAULT_DIR_CACHE);//google建议放到这里
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)//连接失败后是否重新连接
                .connectTimeout(15, TimeUnit.SECONDS)//超时时间15S
                .addNetworkInterceptor(cacheInterceptor)//这里大家一定要注意了是addNetworkOnterceptor别搞错了啊。
                .cache(cache)
                .build();

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值