OKHTTP之缓存配置详解

//缓存文件夹
File cacheFile = new File(getExternalCacheDir().toString(),"cache");
//缓存大小为10M
int cacheSize = 10 * 1024 * 1024;
//创建缓存对象
Cache cache = new Cache(cacheFile,cacheSize);

OkHttpClient client = new OkHttpClient.Builder()
        .cache(cache)
        .build();

设置好Cache我们就可以正常访问了。我们可以通过获取到的Response对象拿到它正常的消息和缓存的消息。

Response的消息有两种类型,CacheResponse和NetworkResponse。CacheResponse代表从缓存取到的消息,NetworkResponse代表直接从服务端返回的消息。示例代码如下:

private void testCache(){
        //缓存文件夹
        File cacheFile = new File(getExternalCacheDir().toString(),"cache");
        //缓存大小为10M
        int cacheSize = 10 * 1024 * 1024;
        //创建缓存对象
        final Cache cache = new Cache(cacheFile,cacheSize);

        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client = new OkHttpClient.Builder()
                        .cache(cache)
                        .build();
                //官方的一个示例的url
                String url = "http://publicobject.com/helloworld.txt";

                Request request = new Request.Builder()
                        .url(url)
                        .build();
                Call call1 = client.newCall(request);
                Response response1 = null;
                try {
                    //第一次网络请求
                    response1 = call1.execute();
                    Log.i(TAG, "testCache: response1 :"+response1.body().string());
                    Log.i(TAG, "testCache: response1 cache :"+response1.cacheResponse());
                    Log.i(TAG, "testCache: response1 network :"+response1.networkResponse());
                    response1.body().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                Call call12 = client.newCall(request);

                try {
                    //第二次网络请求
                    Response response2 = call12.execute();
                    Log.i(TAG, "testCache: response2 :"+response2.body().string());
                    Log.i(TAG, "testCache: response2 cache :"+response2.cacheResponse());
                    Log.i(TAG, "testCache: response2 network :"+response2.networkResponse());
                    Log.i(TAG, "testCache: response1 equals response2:"+response2.equals(response1));
                    response2.body().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
  • 1

打印的结果非常有意思是一个机器人和一个Okhttp的字符串。打印的结果主要说明了一个现象,第一次访问的时候,Response的消息是NetworkResponse消息,此时CacheResponse的值为Null.而第二次访问的时候Response是CahceResponse,而此时NetworkResponse为空。也就说明了上面的示例代码能够进行网络请求的缓存。

那么OKHTTP中的缓存就这么点内容吗?到此为至吗?显然不是。本篇文章开头讲了大段的Http协议中的相关知识点,貌似它们还没有出现。

其实控制缓存的消息头往往是服务端返回的信息中添加的如”Cache-Control:max-age=60”。所以,会有两种情况。 1. 客户端和服务端开发能够很好沟通,按照达成一致的协议,服务端按照规定添加缓存相关的消息头。 2. 客户端与服务端的开发根本就不是同一家公司,没有办法也不可能要求服务端按照客户端的意愿进行开发。

第一种办法当然很好,只要服务器在返回消息的时候添加好Cache-Control相关的消息便好。

第二种情况,就很麻烦,你真的无法左右别人的行为。怎么办呢?好在OKHTTP能够很轻易地处理这种情况。那就是定义一个拦截器,人为地添加Response中的消息头,然后再传递给用户,这样用户拿到的Response就有了我们理想当中的消息头Headers,从而达到控制缓存的意图,正所谓移花接木。

缓存之拦截器

因为拦截器可以拿到Request和Response,所以可以轻而易举地加工这些东西。在这里我们人为地添加Cache-Control消息头。

class CacheInterceptor implements Interceptor{

        @Override
        public Response intercept(Chain chain) throws IOException {

            Response originResponse = chain.proceed(chain.request());

            //设置缓存时间为60秒,并移除了pragma消息头,移除它的原因是因为pragma也是控制缓存的一个消息头属性
            return originResponse.newBuilder().removeHeader("pragma")
                    .header("Cache-Control","max-age=60").build();
        }
    }
  • 1

定义好拦截器中后,我们可以添加到OKHttpClient中了。

private void testCacheInterceptor(){
        //缓存文件夹
        File cacheFile = new File(getExternalCacheDir().toString(),"cache");
        //缓存大小为10M
        int cacheSize = 10 * 1024 * 1024;
        //创建缓存对象
        final Cache cache = new Cache(cacheFile,cacheSize);

        OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(new CacheInterceptor())
                .cache(cache)
                .build();
        .......
}
  • 1

代码后面部分有省略。主要通过在OkHttpClient.Builder()中addNetworkInterceptor()中添加。而这样也挺简单的,就几步完成了缓存代码。

拦截器进行缓存的缺点

网上有人说用拦截器进行缓存是野路子,是HOOK行为。这个我不大同意,前面我有分析过情况,如果客户端能够同服务端一起协商开发,当然以服务器控制的缓存消息头为准,但问题在于你没法这样做。所以,能够解决问题才是最实在的。

好了,回到正题。用拦截器控制缓存有什么不好的地方呢?我们先看看下面的情况。
1. 网络访问请求的资源是文本信息,如新闻列表,这类信息经常变动,一天更新好几次,它们用的缓存时间应该就很短。
2. 网络访问请求的资源是图片或者视频,它们变动很少,或者是长期不变动,那么它们用的缓存时间就应该很长。

那么,问题来了。
因为OKHTTP开发建议是同一个APP,用同一个OKHTTPCLIENT对象这是为了只有一个缓存文件访问入口。这个很容易理解,单例模式嘛。但是问题拦截器是在OKHttpClient.Builder当中添加的。如果在拦截器中定义缓存的方法会导致图片的缓存和新闻列表的缓存时间是一样的,这显然是不合理的,这属于一刀切,就像这两天专家说的要把年收入12万元的人群划分为高收入人群而不区别北上广深的房价物价情况。真实的情况不应该是图片请求有它的缓存时间,新闻列表请求有它的缓存时间,应该是每一个Request有它的缓存时间。
那么,有解决的方案吗?
有的,okhttp官方有建议的方法。

okhttp官方文档建议缓存方法

okhttp中建议用CacheControl这个类来进行缓存策略的制定。
它内部有两个很重要的静态实例。

/**强制使用网络请求*/
public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();

  /**
   * 强制性使用本地缓存,如果本地缓存不满足条件,则会返回code为504
   */
  public static final CacheControl FORCE_CACHE = new Builder()
      .onlyIfCached()
      .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
      .build();
  • 1

我们看到FORCE_NETWORK常量用来强制使用网络请求。FORCE_CACHE只取本地的缓存。它们本身都是CacheControl对象,由内部的Buidler对象构造。下面我们来看看CacheControl.Builder

CacheControl.Builder

它有如下方法:

- noCache();//不使用缓存,用网络请求
- noStore();//不使用缓存,也不存储缓存
- onlyIfCached();//只使用缓存
- noTransform();//禁止转码
- maxAge(10, TimeUnit.MILLISECONDS);//设置超时时间为10ms。
- maxStale(10, TimeUnit.SECONDS);//超时之外的超时时间为10s
- minFresh(10, TimeUnit.SECONDS);//超时时间为当前时间加上10秒钟。
  • 1

知道了CacheControl的相关信息,那么它怎么使用呢?不同于拦截器设置缓存,CacheControl是针对Request的,所以它可以针对每个请求设置不同的缓存策略。比如图片和新闻列表。下面代码展示如何用CacheControl设置一个60秒的超时时间。

private void testCacheControl(){
        //缓存文件夹
        File cacheFile = new File(getExternalCacheDir().toString(),"cache");
        //缓存大小为10M
        int cacheSize = 10 * 1024 * 1024;
        //创建缓存对象
        final Cache cache = new Cache(cacheFile,cacheSize);

        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client = new OkHttpClient.Builder()
                        .cache(cache)
                        .build();
                //设置缓存时间为60秒
                CacheControl cacheControl = new CacheControl.Builder()
                        .maxAge(60, TimeUnit.SECONDS)
                        .build();
                Request request = new Request.Builder()
                        .url("http://blog.csdn.net/briblue")
                        .cacheControl(cacheControl)
                        .build();

                try {
                    Response response = client.newCall(request).execute();

                    response.body().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
  • 1

强制使用缓存

前面有讲CacheControl.FORCE_CACHE这个常量。

public static final CacheControl FORCE_CACHE = new Builder()
      .onlyIfCached()
      .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
      .build();

它内部其实就是调用onlyIfCached()和maxStale方法。
它的使用方法为

Request request = new Request.Builder()
            .url("http://blog.csdn.net/briblue")
            .cacheControl(Cache.FORCE_CACHE)
            .build();

但是如前面后提到的,如果缓存不符合条件会返回504.这个时候我们要根据情况再进行编码,如缓存不行就再进行一次网络请求。

Response forceCacheResponse = client.newCall(request).execute();
     if (forceCacheResponse.code() != 504) {
       // 资源已经缓存了,可以直接使用
     } else {
       // 资源没有缓存,或者是缓存不符合条件了。
     } 

不使用缓存

前面也有讲CacheControl.FORCE_NETWORK这个常量。

public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();
  • 1

它的内部其实是调用noCache()方法,也就是不缓存的意思。
它的使用方法为

Request request = new Request.Builder()
            .url("http://blog.csdn.net/briblue")
            .cacheControl(Cache.FORCE_NETWORK)
            .build();

还有一种情况将maxAge设置为0,也不会取缓存,直接走网络。

Request request = new Request.Builder()
            .url("http://blog.csdn.net/briblue")
            .cacheControl(new CacheControl.Builder()
            .maxAge(0, TimeUnit.SECONDS))
            .build();

总结

本文其实内容不多,前面讲了很多http协议下的缓存机制,我认为是值得的,知道了Cache-Control这些定义,才能更好的懂得OKHTTP中的缓存设置。能够明白为什么它要这样做,为什么它可以这样做。
最后归纳下要点

  • http协议下Cache-Control等消息头的作用
  • okhttp如何用拦截器添加Cache-Control消息头进行缓存定制
  • okhttp如何用CacheControl进行缓存的控制。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值