# OkHttp 缓存分析
在 OkHttp 中使用缓存很方便,OkHttp 内部已经定义好了一套缓存策略,我们在使用的时候,只用在构建 OkHttpClient 的时候指定缓存的的位置和大小就 ok 了,具体的在
[OkHttp 源码解析(二)拦截器原理分析](https://mp.weixin.qq.com/s/T5HOZeiI6vH_MAny9F6tTQ)中已经分析过了,这里再对 OkHttp 的缓存模块进行梳理。
先贴一个经典的 http1.1以后的缓存流程图:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/15834864563737.jpg)
根据图中的信息主要是对一些请求/响应头的判断,这些在 OkHttp 的拦截器中,OkHttp 会进行一定的缓存处理,但是我们也是可以认为的干预,也就是在构建 Request 的时候,进行一些认人为的干预,也就是设置 Request 对象的 cacheControl 属性。比如:
```java
Request request = new Request.Builder()
.url("https://www.qq.com")
.cacheControl(new CacheControl.Builder().noCache().build())
.build();
```
在 OkHttp 的缓存拦截器中会对这里设置的 cacheControl 进行判断,优先级是在上面图中的一些请求/响应头的前面判断的。
下面先来介绍下一些常见的关于缓存的请求头/响应头
## 一些关于缓存的请求头/响应头
### Expires
Expires 表示服务端返回的缓存失效时间。下一次请求时,请求时间小于 Expires 的值,直接使用缓存数据。
由于到期时间是服务端生成,客户端和服务端的时间可能存在误差,导致缓存命中的误差
### Cache-Control
Http1.1 中采用了 Cache-Control 代替了 Expires,常见 Cache-Control 的取值有:
* private
内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存)
* public
所有内容都将被缓存(客户端和代理服务器都可缓存)
* max-age=xxx
缓存的内容将在 xx 秒后失效,这个选项只在HTTP 1.1可用, 并如果和Last-Modified一起使用时, 优先级较高
* no-cache
必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。
需要使用对比缓来验证数据(并不是字面上的不使用缓存)
* no-store
所有内容都不缓存。 - **不会触发** 强制缓存,对比缓存
### Last-Modified 和 If-Modified-since
* Last-Modified 服务器返回的 缓存控制字段
* If-Modified-since 如果支持缓存 ,在进行 http 请求的时候,If-Modified-since 会使用上次请求返回的 last-modified值,作为客户端向服务端请求的时的请求头,
### ETag 和 If-None-Match
* ETag 是服务器返回响应的唯一标识
* if-none-match 如果支持缓存 ,在进行 http 请求的时候,if-none-match 会使用上次请求返回的 etag 值,作为客户端向客户端请求时的请求头。
## OkHttp 缓存处理流程图
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/OkHttp缓存机制.png)
大体上的流程如下:
1. 如果用户设置了缓存,先拿到上次请求的**候选缓存**
2. 根据**候选缓存**获取OkHttp 的缓存策略
3. 如果**候选缓存为空**,则返回请求request不为空,**不使用缓存**,**使用网络请求**
4. 如果是 https 并且握手信息为空,**不使用缓存,使用网络请求**
5. 如果判断请求和候选缓存的cache-control 字段是否是 no-store,如果是,**不使用缓存,使用网络请求**
6. 判断request 的cacheControl 属性是否设置了不支持缓存或者本次请求中的是否包含了 if-modified-since或者 if-none-match请求头,如果不支持或者有两个请求头中的任何一个,**不使用缓存,使用网络请求**
7. 判断候选缓存的 cacheControl,如果候选缓存中的 cacheControl 支持缓存或者缓存未过期,**使用缓存,不请求网络**
8. 根据候选缓存中的 etag、last-modified、Date请求头字段,分别设置本次请求 Request 的if-none-match、if-modified-since、if-modified-since字段,以此往后,只要有一个,后面的请求头不会被设置,这时返回本次**请求 networkRequest 和 缓存 cacheResponse 都不为空。**
9. 经过上面已经拿到了 OkHttp 中的缓存策略。此时如果请求为空,候选缓存为空,**则构建 Response,抛出 504(网关超时)错误。return 结束**
10. 如果本次请求为null,**使用本地缓存(走到这里,缓存一定不为空)return 结束**
11. 这里表示networkRequest 和 缓存 cacheResponse 都不为空,那么代表要请求网络,继续请求下个拦截去,然后拿到下个拦截器返回的 response 为 netwoekResponse。
12. 如果返回的 networkResponse 的**状态码为 304,表示请求的响应未修改,那么继续使用本地缓存,更新本地缓存。 return结束**
13. 如果返回的 networkResponse 有 body并且本次请求和本次网络返回是被允许缓存的,则**更新本地缓存并return结束**
14. 如果本次请求是不是 GET(或者是不支持缓存的请求类型),那么**移除本地对应 url 的请求缓存。return结束。**
整体上就是这么个流程,如果是要了解 OkHttp 的缓存机制,那么必然要先了解通用的缓存原理,再来分析 OkHttp 的缓存原理,要不然只有一脸懵逼的份。
推荐文章:
[OKHTTP之缓存配置详解](https://blog.csdn.net/briblue/article/details/52920531)
[HTTP 缓存机制一二三](https://zhuanlan.zhihu.com/p/29750583)
[彻底弄懂HTTP缓存机制及原理](https://www.cnblogs.com/chenqf/p/6386163.html)
- Java 面试题
- String、StringBuffer、StringBuilder 的区别?
- Java 中的四种引用
- 接口和抽象类的本质区别
- 集合框架
- 集合概述
- ArrayList 源码分析
- LinkedList 源码分析
- HashMap 源码分析
- LinkedHashMap 源码分析
- Android提供的 LruCache 的分析
- LinkedList 和 ArrayList 的区别
- 多线程
- 实现多线程的几种方式
- 线程的几种状态
- Thread 的 start() 和 run() 的区别
- sleep() 、yield() 和 wait() 的区别 ?
- notify() 和 notifyAll() 的区别?
- 保证线程安全的方式有哪几种?
- Synchronized 关键字
- volatile 和 synchronized 的区别?
- 如何正确的终止一个线程?
- ThreadLocal 原理分析
- 线程池
- 多线程的三个特征
- 五种线程池,四种拒绝策略,三种阻塞队列
- 给定三个线程如何顺序执行完以后在主线程拿到执行结果
- Java 内存模型
- 判定可回收对象算法
- equals 与 == 操作符
- 类加载机制
- 类加载简单例子
- 算法
- 时间、空间复杂度
- 冒泡排序
- 快速排序
- 链表反转
- IO
- 泛型
- Kolin 面试题
- Android 面试题
- Handler 线程间通信
- Message、MessageQueue、Looper、Handler 的对象关系
- Handler 使用
- Handler 源码分析
- HandlerThread
- AsyncTask
- IntentService
- 三方框架
- Rxjava
- rxjava 操作符有哪些
- 如何解决 RxJava 内存泄漏
- Rxjava 线程切换原理
- map和 flatmap 的区别
- Databinding引起的 java方法大于 65535 的问题
- Glide
- Glide 的缓存原理
- Glide 是如何和生命周期绑定的?不同的Context 有什么区别?
- Glide 、Picasso 、的区别,优劣势,如何选择?
- Jetpack
- 源码分析
- EventBus
- EventBus 源码分析
- RxBus 替代 EventBus
- OkHttp
- OkHttp 源码分析
- OkHttp 缓存分析
- RxPermission
- RxPermission 源码分析
- Retrofit
- create
- Retrofit 源码分析
- 优化
- 启动优化
- 布局优化
- 绘制优化
- 内存优化
- 屏幕适配
- 组件
- Activity
- Frgment
- Service
- ContentProvider
- BroadcastReceiver
- 进程间通信
- Binder机制和AIDL
- AILD 中的接口和普通的接口有什么区别
- in、out、inout 的区别
- Binder 为什么只需要拷贝一次
- 在android中,请简述jni的调用过程
- 生命周期
- Activity 生命周期
- Fragment 生命周期
- Service 生命周期
- onSaveInstanceState() 与 onRestoreIntanceState()
- 前沿技术
- 组件化
- 模块化
- 插件化
- 热更新
- UI - View
- Android 动画
- 事件分发机制
- WebView
- 系统相关
- 谈谈对 Context 的理解
- Android 版本
- App应用启动流程
- App 的打包
- App 的加固
- App 的安装
- Activity 启动流程
- ClassLoader
- Lru 算法加载 Bitmap 三级缓存原理
- Parcelable 和 Serializable 的区别
- Activity的启动流程
- 相关概念
- 网络相关
- Http
- Https
- Http 和 Https 的区别
- 为什么要进行三次握手和四次挥手?
- OkHttp使用Https访问服务器时信任所有证书
- 设计模式
- 单例模式
- 构建者模式
- 工厂模式
- 外观模式
- 代理模式