缓存:
服务器支持缓存
如果服务器支持缓存,请求返回的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卡的绝对路径
File path = Environment.getExternalStorageDirectory().getAbsoluteFile();
//创建缓存文件位置及名称
File file = new File(path,"/jiyun");
//创建缓存对象,设置缓存大小
Cache cache = new Cache(file,1024*1024*10);
OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClient newClient = okHttpClient.newBuilder()
.cache(cache)
//连接超时时间
.connectTimeout(20, TimeUnit.SECONDS)
//读取超时时间
.readTimeout(20, TimeUnit.SECONDS)
//添加应用拦截器
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
//通过Chain获取请求对象
Request request = chain.request();
//通过请求对象添加缓存控制类的属性
Request build = request.newBuilder().cacheControl(new
//设置缓存本地时间为10 毫秒(maxAge())
CacheControl.Builder().maxAge(10, TimeUnit.MILLISECONDS).build()).buidl;
//通过Chain获取响应对象并return
return proceed = chain.proceed(request);
}
});
出于安全性的考虑,在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: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的配置:
指令 | 说明 |
---|---|
public | 所有内容都被缓存 |
private | 仅客户端缓存代理服务器不缓存 |
no-cache | 必须先与代理服务器确认是否更改,然后在在决定使用缓存还是请求 |
no-store | 所有内容都不会被缓存 |
must-revalidation/proxy-revalidation | 如果缓存内容失效,请求必须发送服务器/代理进行验证 |
max-age=<s> | 缓存内容在s秒后失效,仅HTTP1.1可用 |
对于上述配置浏览器的响应为:
指令 | 打开新窗口 | 原窗口单击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();