OkHttp 缓存机制总览
本章从宏观视角梳理 OkHttp 磁盘缓存的整体架构。理解核心类、默认行为、缓存开关及拦截器链,是后续配置缓存参数、优化性能、排查故障的基础。
1.1 核心类与职责
OkHttp 的磁盘缓存由以下四个核心类协作完成:
| 类名 | 职责 | 关键点 |
|---|---|---|
Cache |
管理磁盘缓存的读写、过期清理与大小控制 | 基于 DiskLruCache 实现,提供线程安全的缓存存取 |
CacheStrategy |
根据请求和缓存响应,决策使用缓存、发起网络请求或组合策略 | 核心方法是 get(),返回包含缓存策略的 CacheStrategy 对象 |
CacheInterceptor |
作为 OkHttp 拦截器链中的一环,执行缓存读写逻辑 | 是连接 Cache 和网络请求的桥梁 |
InternalCache |
OkHttp 内部对 Cache 的封装接口 |
开发者通常不直接操作,但了解其存在有助于理解源码 |
1.2 默认行为
OkHttp 在未显式配置缓存时,默认不启用磁盘缓存。此时所有请求都直接走网络,响应不会被持久化。若要启用缓存,必须满足两个条件:
- 在
OkHttpClient.Builder上调用cache()方法并传入有效的Cache对象。 - 服务端响应必须包含允许缓存的
Cache-Control头。
核心原则:即使配置了 Cache 对象,如果服务端返回的
Cache-Control: no-cache, no-store 或 max-age=0,缓存也不会生效。客户端缓存策略受服务端约束。
1.3 缓存开关:配置与禁用
通过 Cache 构造器指定缓存目录和最大大小,即可开启磁盘缓存:
// 创建缓存目录
File cacheDir = new File(context.getCacheDir(), "okhttp_cache");
long maxSize = 10 * 1024 * 1024; // 10 MB
// 构造 Cache 对象
Cache diskCache = new Cache(cacheDir, maxSize);
// 配置到 OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.cache(diskCache)
.build();
禁用缓存 有两种方式:
- 全局禁用:不调用
.cache(),或传入null。 - 单请求禁用:在
Request.Builder上调用.cacheControl(CacheControl.FORCE_NETWORK)。
// 单请求强制走网络
Request request = new Request.Builder()
.url("https://api.example.com/data")
.cacheControl(CacheControl.FORCE_NETWORK)
.build();
1.4 拦截器链中的缓存位置
CacheInterceptor 位于 OkHttp 内置拦截器链的 第二层,紧跟在 BridgeInterceptor 之后、ConnectInterceptor 之前。其执行逻辑分为两个阶段:
- 读取阶段:从磁盘缓存中查找匹配的缓存条目,交由
CacheStrategy判断是否可用。 - 写入阶段:网络响应返回后,判断是否可缓存,并写入磁盘。
下图展示了请求在拦截器链中的完整流经顺序及缓存决策的介入点:
1.5 缓存策略决策矩阵
CacheStrategy 根据 Request 和缓存的 Response 输出一种策略,常见三种结果:
| 策略结果 | 触发条件 | 行为 |
|---|---|---|
| FULL_CACHE | 缓存未过期,且满足新鲜度条件 | 直接使用缓存响应,不发起网络请求 |
| NETWORK | 缓存已过期或不存在,或强制网络 | 发起完整网络请求,响应可替换旧缓存 |
| CONDITIONAL_CACHE | 缓存过期但服务端资源可能未变 | 发送带 If-Modified-Since 或 If-None-Match 的条件请求 |
性能陷阱:当缓存策略为
CONDITIONAL_CACHE 时,网络请求仍然会发出,但服务端若返回 304 Not Modified,则响应体为空,仅更新缓存标头。这种情况既消耗了网络延迟,又无法完全避免服务器处理,应在业务设计时尽量减少条件缓存的触发频率。
1.6 小结与调试切入点
理解以上四个核心概念后,后续章节将逐一深入:
- Cache 对象创建:目录选择、大小配置、版本管理。
- 缓存控制头:
Cache-Control指令详解与自定义策略。 - 拦截器调试:通过日志拦截器观察请求是否命中缓存。
- 性能优化:缓存命中率分析、锁竞争消除、I/O 瓶颈定位。
建议读者在集成缓存后,使用 OkHttp 提供的 EventListener 或标准日志拦截器,监控每次请求的 cacheHit 事件,以验证缓存逻辑是否正确。