Okhttp----缓存的加入方式, cache-control说明

转自:http://blog.csdn.net/copy_yuan/article/details/51524907

Okhttp是由Sqare公司开发的开源网络访问库,目前在Android和Java开发中有着广泛的应用。在Android开发中和Retrofit结合可以非常方便地调用网络接口。

使用缓存可以让我们的app不用长时间地显示令人厌烦的加载圈,提高了用户体验,而且还节省了流量,在数据更新不是很频繁的地方使用缓存就非常有必要了。想要加入缓存不需要我们自己来实现,Okhttp已经内置了缓存,默认是不使用的,如果想使用缓存我们需要手动设置。

服务器支持缓存

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

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

1
2
3
public Cache provideCache() {
        return new Cache(mContext.getCacheDir(), 10240*1024);
    }

创建了这个缓存后我们还需要将其设置到okttpClient对象里面:

1
2
3
4
5
6
OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClient newClient = okHttpClient.newBuilder()
               .cache(cache)
               .connectTimeout(20, TimeUnit.SECONDS)
               .readTimeout(20, TimeUnit.SECONDS)
               .build();

服务器不支持缓存

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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中:

1
2
3
4
5
6
7
8
 OkHttpClient okHttpClient = new OkHttpClient();

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

这样我们就可以在服务器不支持缓存的情况下使用缓存了。

完整代码见github:    

A simple Android app shows Hearthstone cards with okhttp, retrofit, dagger2 and Meterail Design 

地址:https://github.com/Chaoba/HearthstoneCards

CacheControl的参数说明

cache-control的值比较复杂,下面参考RFC文档和MDN网站罗列出的结果。

请求头

max-age

客户端不愿意接受age超过这个值的缓存。并且不接受过期缓存,除非max-stale存在。 
总是有值

max-stale

如果有值,客户端可以接受过期时间不超过指定值的缓存 
如果没有值,客户端愿意接受过期缓存而无论过期过久。

min-fresh

客户端愿意接受一个新鲜度不小于当前age加上指定时间的响应。简单说在指定的后续一段时间内不会过期的响应。 
总是有值

no-cache

客户端示意缓存,在使用缓存的时候必须进行校验。 
没有值

no-store

客户端示意缓存,不要存储本次请求的响应。但是对于已经缓存的内容则没有影响。 
也就说缓存可以从已缓存的数据中构造本次响应。这里难以理解,下面是rfc7234中关于这部分描述的片段的片段

https://tools.ietf.org/html/rfc7234#section-5.2.1 
Note that if a request containing this directive is satisfied from a 
cache, the no-store request directive does not apply to the already 
stored response.

https://tools.ietf.org/html/rfc7234#appendix-A 
The “no-store” request directive doesn’t apply to responses; i.e., a 
cache can satisfy a request with no-store on it and does not 
invalidate it. 
没有值

no-transform

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control 
不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。 no-transform指令不允许这样做。 
没有值

only-if-cached

客户端只接受缓存给出的响应,如果缓存没有命中应该返回一个504 
MDN的中文翻译在这里描述的是“如果缓存存在,…“,对比英文版以及RFC7234中的描述,这个描述是错误的,我已在MDN上进行修改

https://tools.ietf.org/html/rfc7234#section-5.2.1 
If it receives this directive, a cache SHOULD either respond using a stored response that is 
consistent with the other constraints of the request, or respond with 
a 504 (Gateway Timeout) status code.

响应头

must-revalidate

一旦缓存过期,必须向源服务器进行校验,不得使用过期内容。如果无法连接必须返回504。 
没有值

no-cache

如果值,在没有成功通过源站校验的情况下不得使用缓存。 
有值,在进行验证的时候不要发送值指示的头域。 
Cache-Control: no-cache="set-cookie,set-cookie2",表示不要携带cookie进行验证。 
关于带有值的情况介绍较少,这里有一个讨论:no-cache头带值时

no-store

不要缓存当前请求的响应 
没有值

no-transform

与请求头语义相同

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control 
不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。 no-transform指令不允许这样做。 
没有值

public

任何缓存都可以进行缓存,即使响应默认是不可缓存或仅私有缓存可存的情况。

private

没有值,公有缓存不可存储;即使默认是不可缓存的,私有缓存也可以存储 
有值,将无值时的作用,限制到指定头字段上。公有有缓存不可存储指定的头字段,而其他字段可以缓存。

proxy-revalidate

与must-revalidate相同,但仅对公共缓存生效。 
没有值

max-age

在经过指定时间后将过期 
有值

s-maxage

指定响应在公共缓存中的最大存活时间,它覆盖max-age和expires字段。


3. okhttp缓存的简单使用


OkHttp中的缓存整体上来说我们要在两个地方配置,一个是构造OkHttpClient时,还有一个是在构造Request时

1.在OkHttpClient构造时设置缓存路径

我们在使用OkHttp的时候,一般都会将client的获取封装起来,因为在大多数情况下,我们需要的OkHttpClient其实都是一样的。在封装的过程中,我们可以设置很多属性,比如链接超时时间、读取超时时间等,其中也包括我们即将要说的cache,我们可以在这里来配置cache路径,配置了cache路径之后,OkHttp请求到的数据就会缓存到该路径下,当手机没有联网的时候,就可以直接从缓存中加载数据。我们来看看代码:

[java]  view plain copy print ?
  1. OkHttpClient client = new OkHttpClient.Builder()  
  2.                 .connectTimeout(5, TimeUnit.SECONDS)  
  3.                 .cache(new Cache(new File(this.getExternalCacheDir(), "okhttpcache"), 10 * 1024 * 1024))  
  4.                 .build();  

我这里是设置了缓存的路径为 ~/mnt/sdcard/Android/data/应用包名/cache/okhttpcache ,第二个参数表示缓存区的大小为10M,当缓存区的数据大小超过10M的时候会自动删除已缓存的数据,当我们配置了缓存路径之后,当我发起一个网络 请求之后,如下:

[java]  view plain copy print ?
  1. Request request = new Request.Builder().url("http://www.tngou.net/api/food/list?id=1").build();  
  2. client.newCall(request).enqueue(new Callback() {  
  3.     @Override  
  4.     public void onFailure(Call call, IOException e) {  
  5.   
  6.     }  
  7.   
  8.     @Override  
  9.     public void onResponse(Call call, Response response) throws IOException {  
  10.         if (response.isSuccessful()) {  
  11.             sb = new StringBuffer();  
  12.             try {  
  13.                 JSONObject jo = new JSONObject(response.body().string());  
  14.                 JSONArray tngou = jo.optJSONArray("tngou");  
  15.                 for (int i = 0; i < tngou.length(); i++) {  
  16.                     sb.append(tngou.optJSONObject(i).optString("name")).append("\n");  
  17.                 }  
  18.                 runOnUiThread(new Runnable() {  
  19.                     @Override  
  20.                     public void run() {  
  21.                         tv.setText(sb.toString());  
  22.                     }  
  23.                 });  
  24.             } catch (JSONException e) {  
  25.                 e.printStackTrace();  
  26.             }  
  27.         }  
  28.     }  
  29. });  

请求完毕之后,用一个TextView将请求结果显示出来,当请求完毕之后,在我们上面所说的那个目录下,可以看到如下三个文件:

这里有三个文件,其中以.0结尾的文件缓存了http的响应头信息,以.1结尾的文件则缓存了我们下载的json数据,journal则是一个日志文件,我们把这几个文件打开来看看:

.0


.1


journal


OK,配置了cache之后,当我们请求过一次数据之后,然后关闭掉网络,这个时候再去请求网络数据,这个时候OkHttp会自动从本地缓存中重新加载数据。

2.构造Request时配置缓存策略

上面的配置应该已经可以满足许多小伙伴的需求了,可是很多时候我们还有许多其他的需求,那么这些需求我们可以在构造Request的时候通过CacheControl来进行进一步的配置。

在构造Request的时候,我们可以配置CacheControl,配置有两种方式,一种是构造CacheControl,还有一种是直接使用CacheControl中的常量,我们来分别看一下:

2.1构造CacheControl

[java]  view plain copy print ?
  1.         CacheControl cc = new CacheControl.Builder()  
  2.                 //不使用缓存,但是会保存缓存数据  
  3.                 //.noCache()  
  4.                 //不使用缓存,同时也不保存缓存数据  
  5.                // .noStore()  
  6.                 //只使用缓存,(如果我们要加载的数据本身就是本地数据时,可以使用这个,不过目前尚未发现使用场景)  
  7.                 //.onlyIfCached()  
  8.                 //手机可以接收响应时间小于当前时间加上10s的响应  
  9. //                .minFresh(10,TimeUnit.SECONDS)  
  10.                 //手机可以接收有效期不大于10s的响应  
  11. //                .maxAge(10,TimeUnit.SECONDS)  
  12.                 //手机可以接收超出5s的响应  
  13.                 .maxStale(5,TimeUnit.SECONDS)  
  14.                 .build();  
  15.         Request request = new Request.Builder()  
  16.                 .cacheControl(cc)  
  17.                 .url("http://192.168.152.2:8080/cache").build();  

这个用起来还是比较简单的,没什么好说的,重要代码看注释。

2.2使用CacheControl中的常量 

如果直接使用CacheControl中的常量,则不用调用上面那么多的方法,使用方式如下:

[java]  view plain copy print ?
  1.         Request request = new Request.Builder()  
  2.                 //强制使用网络  
  3. //                .cacheControl(CacheControl.FORCE_NETWORK)  
  4.                 //强制使用缓存  
  5.                 .cacheControl(CacheControl.FORCE_CACHE)  
  6.                 .url("http://192.168.152.2:8080/cache").build();  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值