OkHttp 缓存机制总览

本章从宏观视角梳理 OkHttp 磁盘缓存的整体架构。理解核心类、默认行为、缓存开关及拦截器链,是后续配置缓存参数、优化性能、排查故障的基础。

1.1 核心类与职责

OkHttp 的磁盘缓存由以下四个核心类协作完成:

类名 职责 关键点
Cache 管理磁盘缓存的读写、过期清理与大小控制 基于 DiskLruCache 实现,提供线程安全的缓存存取
CacheStrategy 根据请求和缓存响应,决策使用缓存、发起网络请求或组合策略 核心方法是 get(),返回包含缓存策略的 CacheStrategy 对象
CacheInterceptor 作为 OkHttp 拦截器链中的一环,执行缓存读写逻辑 是连接 Cache 和网络请求的桥梁
InternalCache OkHttp 内部对 Cache 的封装接口 开发者通常不直接操作,但了解其存在有助于理解源码

1.2 默认行为

OkHttp 在未显式配置缓存时,默认不启用磁盘缓存。此时所有请求都直接走网络,响应不会被持久化。若要启用缓存,必须满足两个条件:

  1. 在 OkHttpClient.Builder 上调用 cache() 方法并传入有效的 Cache 对象。
  2. 服务端响应必须包含允许缓存的 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 判断是否可用。
  • 写入阶段:网络响应返回后,判断是否可缓存,并写入磁盘。

下图展示了请求在拦截器链中的完整流经顺序及缓存决策的介入点:

OkHttp 拦截器链与缓存决策流程 客户端请求 应用层拦截器 BridgeInterceptor CacheInterceptor 缓存读写决策 ConnectInterceptor 网络请求/服务端 缓存决策分支 命中 → 直接返回 未命中 → 走网络 条件缓存 → 发验证请求

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 事件,以验证缓存逻辑是否正确。