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是什么?addInterceptoraddNetworkInterceptor区别是什么?等等问题,别着急,逐个击破。

Interceptor是什么?

中文意思“拦截器”。拦截器可以拿到网络请求的 Request 对象和 Response 对象,有了这两个对象我们就可以对网络请求进行监听(打印日志)、缓存、修改 HTTP 的请求报文和响应报文。

 

addInterceptor()和addNetworkInterceptor()区别是什么?

1、Application Interceptor

  • 使用 addInterceptor() 注册
  • 有无网络都会被调用到
  • application 拦截器只会被调用一次,调用 chain.proceed() 得到的是重定向之后最终的响应信息,不会通过 chain.connection() 获得中间过程的响应信息
  • 允许 short-circuit (短路) 并且允许不去调用 chain.proceed() 请求服务器数据,可通过缓存来返回数据。
2、Net Interceptor
  • 使用 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 }

 

有错误的地方还请指出!

参考:https://www.jianshu.com/p/1752753db538 

posted @ 2021-12-21 17:56  渣娃  阅读(589)  评论(0编辑  收藏  举报