OkHttp缓存整理
前段时间工作中遇到点问题:使用OKHttp框架有网络时启动APP正常请求及加载页面,无网时启动APP不读取缓存,页面什么都不显示。百度了一大堆帖子,说啥的都有,试了好多还是不行,最后还是请教了一下好友才解决此问题,特此记录一下。
OKHttp无网启动app直接读取缓存主要包括三点:
1、定义无网请求拦截器
1 /** 2 * 无网缓存拦截器,有网没网都会调用 3 */ 4 static class OfflineCacheInterceptor implements Interceptor{ 5 6 @Override 7 public Response intercept(Chain chain) throws IOException { 8 9 Request.Builder builder = chain.request().newBuilder(); 10 if (!NetWorkUtils.checkNetworkStatus(getApplication())){ 11 Log.i("ZhidaApplication","No network, forced read cache!"); 12 builder.cacheControl(CacheControl.FORCE_CACHE); 13 } 14 Request request = builder.build(); 15 16 return chain.proceed(request); 17 } 18 }
2、定义有网请求拦截器
1 /** 2 * 有网缓存拦截器,有网的时候才会调用,没网不会调用 3 */ 4 static class NetCacheInterceptor implements Interceptor{ 5 6 @Override 7 public Response intercept(Chain chain) throws IOException { 8 9 Response response = chain.proceed(chain.request()).newBuilder() 10 .removeHeader("Pragma") 11 .removeHeader("Cache-Control") 12 .addHeader("Cache-Control",CacheControl.FORCE_CACHE.toString()).build(); 13 14 return response; 15 } 16 }
3、配置HttpClient
1 // 初始化网络请求 加入两种网络缓存机制减轻服务器压力 2 File httpCacheDirectory = new File(getFilesDir(), "okhttpCache");//设置缓存路径 3 int cacheSize = 10 * 1024 * 1024; // 设置缓存大小为10 MiB 4 Cache cache = new Cache(httpCacheDirectory, cacheSize); 5 OkHttpClient.Builder builder = new OkHttpClient.Builder(); 6 builder.connectTimeout(5000, TimeUnit.MILLISECONDS)//设置连接超时时间 7 .readTimeout(5000, TimeUnit.MILLISECONDS)//设置读取超时时间 8 .retryOnConnectionFailure(true)//开启自动重新请求 9 .addInterceptor(new OfflineCacheInterceptor())//添加无网拦截器 10 .addNetworkInterceptor(new NetCacheInterceptor())//添加有网拦截器 11 .cache(cache);//设置缓存 12 OkHttpUtils.initClient(builder.build());
写到这里无网时启动APP就可以正常读取缓存了,刚需的同学直接拿去用,想了解更多的童鞋接着往下看
↓↓↓
到这里有很多同学有会疑问,Interceptor是什么?addInterceptor和addNetworkInterceptor区别是什么?等等问题,别着急,逐个击破。
Interceptor是什么?
中文意思“拦截器”。拦截器可以拿到网络请求的 Request 对象和 Response 对象,有了这两个对象我们就可以对网络请求进行监听(打印日志)、缓存、修改 HTTP 的请求报文和响应报文。
addInterceptor()和addNetworkInterceptor()区别是什么?
1、Application Interceptor
- 使用 addInterceptor() 注册
- 有无网络都会被调用到
- application 拦截器只会被调用一次,调用 chain.proceed() 得到的是重定向之后最终的响应信息,不会通过 chain.connection() 获得中间过程的响应信息
- 允许 short-circuit (短路) 并且允许不去调用 chain.proceed() 请求服务器数据,可通过缓存来返回数据。
- 使用 addNetworkInterceptor() 注册
- 无网络时不会被调用
- 可以显示更多的信息,比如 OkHttp 为了减少数据的传输时间以及传输流量而自动添加的请求头 Accept-Encoding: gzip 希望服务器能返回经过压缩过的响应数据。
- chain.connection() 返回不为空的 Connection 对象可以查询到客户端所连接的服务器的IP地址以及TLS配置信息。
响应头(.header)里的参数都代表什么?
- Expires 首部
- HTTP/1.0+ 时服务器用来指定过期日期的首部,其使用的是绝对日期。现在更常用的是使用HTTP/1.1 的 Cache-Control 首部代替,其使用的是相对日期。
- Pragma 首部
- 用来包含实现特定的指令,最常用的是 Pragma:no-cache。在 HTTP/1.1 协议中,它的含义和 Cache-Control:no-cache 相同。
- Cache-Control 首部
- 位于通用报头,用来指定缓存的指令。缓存指令是单向的(响应时出现的缓存指令在请求是未必会出现)并且也是独立的(一个消息处理的缓存指令不会影响另一个消息处理的缓存机制)。下面是一些会用到的指令。
- public : 指示响应可被任何缓存区缓存。
- private : 指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当前用户的部分响应消息,此响应消息对于其他用户的请求无效。
- max-age : 定义了缓存过期时间(从第一次生成文档到无法使用为止)单位为秒,没有超出则不管怎么样都是返回缓存数据,超出了则发起新的请求获取数据更新,请求失败返回缓存数据。
- max-stale : 定义了强制使用缓存的时间,单位为秒。没有超过则不管怎么样都返回缓存数据,超过了则发起请求获取更新数据,请求失败返回失败 (response.code() 为 504)。
- only-if-cached : 只使用缓存
- no-cache : 指示请求或响应消息不能缓存
- no-store : 用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
.header()设置缓存和 .cacheControl() 设置缓存的区别?
header写法
1 request = request.newBuilder() 2 .header("Cache-Control", "public, only-if-cached, max-stale=" + offlineCacheTime) 3 .build();
CacheControl写法
1 request = request.newBuilder() 2 .cacheControl(CacheControl.FORCE_CACHE) 3 .build();
header写法是通过静态的方式去请求缓存。CacheControl是强制获取缓存,也是官方文档中推荐的。至于二者区别,我还没有深究,有知道的区别童鞋,还请指教!
OKHttp默认只支持Get请求进行缓存,Post请求可以缓存么?
当然可以,我们只需要在无网拦截器中做下判断,详见以下代码
1 public class OfflineCacheInterceptor implements Interceptor { 2 private static final CacheControl FORCE_MY_CACHE = new CacheControl.Builder() 3 .onlyIfCached() 4 .maxStale(InterceptorConfiguration.OFFLINE_CACHE_TIME, TimeUnit.SECONDS) 5 .build(); 6 7 @Override 8 public Response intercept(Chain chain) throws IOException { 9 InterceptorConfiguration.debugLog("无网缓存拦截器开始"); 10 11 Request request = chain.request(); 12 Response response = null; 13 14 String requestUrl = InterceptorConfiguration.getUrl(request); 15 String method = request.method(); 16 if (!NetState.isNetworkConnected(MyApplication.getContext())) { 17 request = request.newBuilder().cacheControl(FORCE_MY_CACHE).build(); 18 19 if ("GET".equals(method)) { 20 response = chain.proceed(request); 21 } else if ("POST".equals(method)) { 22 InterceptorConfiguration.debugLog("读取POST缓存"); 23 HttpDbBean httpDbBean = HttpDbBean.getByUrl(requestUrl); 24 if (httpDbBean != null && !TextUtils.isEmpty(httpDbBean.getResponse())) { 25 String b = httpDbBean.getResponse(); 26 byte[] bs = b.getBytes(); 27 28 response = new Response.Builder() 29 .request(request) 30 .body(ResponseBody.create(MediaType.parse("application/json"), bs)) 31 .message(httpDbBean.getMessage()) 32 .code(httpDbBean.getCode()) 33 .protocol(Protocol.HTTP_1_1) 34 .build(); 35 } 36 } 37 } else { 38 response = chain.proceed(request); 39 String responseBodyString = InterceptorConfiguration.getResponseBody(response); 40 41 if ("POST".equals(method)) { 42 // 保存请求信息 43 LitePal.deleteAll(HttpDbBean.class, "url = ?", requestUrl); 44 HttpDbBean saveHttpBean = new HttpDbBean(requestUrl, responseBodyString, response.message(), response.code()); 45 saveHttpBean.save(); 46 } 47 } 48 49 InterceptorConfiguration.debugLog("无网缓存拦截器结束"); 50 // 为空则抛异常交给调用层的回掉处理 51 if (response == null) throw new IOException() { 52 @Override 53 public String getMessage() { 54 return "The requested address is empty in the database"; 55 } 56 }; 57 return response; 58 } 59 }
有错误的地方还请指出!