1、HTTP缓存基础与Retrofit拦截器概览

在深入 Retrofit 拦截器配置之前,必须先奠定 HTTP 缓存的理论基石。HTTP 缓存机制决定了客户端(App)能否复用先前请求的响应,从而避免重复发起网络请求。OkHttp 的拦截器体系正是操纵这些缓存行为的“插拔点”。

1.1 HTTP 缓存核心头

HTTP/1.1 定义了强制缓存和协商缓存两种策略。两者依赖的响应头不同,优先级也不同。

缓存类型 关键响应头 决策逻辑
强制缓存(本地判断) Cache-Control: max-age=3600 计算当前时间与 Date 头差值,若 < max-age,直接使用本地缓存
强制缓存(过期) Expires: Thu, 01 Dec 2025 16:00:00 GMT 依赖客户端时钟精确性;若 max-age 同时存在,则 max-age 优先
协商缓存(去服务端验证) ETag: "abc123" 客户端发送 If-None-Match 头,服务端返回 304 Not Modified 或完整资源
协商缓存(时间验证) Last-Modified: Wed, 15 Nov 2025 12:00:00 GMT 客户端发送 If-Modified-Since,服务端对比时间决定 304 或 200

1.2 缓存策略分类与决策流程

OkHttp 内置的 CacheStrategy 类将上述头组合成三种最终决策:

  • Network Only:仅从网络获取,不写缓存。适用实时数据。
  • Cache Only:仅从本地缓存读取,不发起网络请求。适用离线模式。
  • Cache + Network:优先读缓存,若过期则走协商缓存或强制请求。
关键门槛:只有理解了 CacheStrategy 的“请求—缓存—网络”三角关系,才能精确控制Interceptor 中的缓存替换逻辑。
HTTP 缓存决策流程(OkHttp 视角) 客户端请求 缓存拦截器读缓存 强缓存 命中? 是 直接返回缓存 否 发起网络请求 服务端协商(304 或 200)

1.3 Interceptor 在 OkHttp 中的地位

OkHttp 的拦截器分为两类,Application Interceptor(应用拦截器)与 Network Interceptor(网络拦截器)。缓存拦截器通常注册为应用拦截器,在请求发出前拦截并判断缓存策略;或在响应返回后更新缓存。

// 典型应用拦截器注册方式
OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(new CacheInterceptor()) // 自定义缓存拦截器
        .cache(new Cache(context.getCacheDir(), 10 * 1024 * 1024)) // 10MB缓存目录
        .build();
拦截器执行顺序:应用拦截器按照添加顺序依次执行,网络拦截器则按逆序执行。缓存拦截器建议放在应用层拦截器的最前端,以确保在写入缓存前有机会修改响应头。

1.4 课程最终交付物清单

通过本课程 30 章节的系统学习,您将获得以下可直接用于生产的交付物:

交付物 内容描述 适用场景
CacheInterceptor 可拔插的 OkHttp 应用层拦截器,支持 max-age、ETag 自定义解析 通用项目,需要精细控制缓存过期
网络策略注解 @CacheStrategy 注解 + CachePolicy 枚举 多接口差异化缓存需求
日志/调试辅助模块 将缓存命中和失败 log 输出到控制台,带耗时和 header 详情 日常开发和线上问题排查
性能压测脚本 基于 Gradle Task 的并发缓存压力测试命令 上线前缓存稳定性验证
常见错误清单 包括 NO_CACHE 被忽略、Cache-Control 冲突等 10+ 超时案例 团队协作与代码 Review

1.5 为什么从缓存拦截器开始

OkHttp 本身提供了 CacheInterceptor 实现,但其内置策略强制遵循 RFC 7234,无法满足以下常见需求:

  • 离线优先:即使服务端返回 Cache-Control: no-store,仍希望本地缓存可用
  • 自定义过期时间:根据业务代码动态设置 max-age,而非固定头
  • 缓存预热:App 启动时从缓存中读取已知数据,避免白屏
  • 流量节省:对图片等大资源强制使用协商缓存,降低带宽消耗

因此,掌握自定义缓存拦截器的原理和实现,是 Android 高级工程师流畅处理网络层的关键能力。

常见陷阱:不要混淆“内存缓存”与“磁盘缓存”。OkHttp 的 Cache 类默认使用磁盘持久化,而 CacheInterceptor 负责控制读写策略。如果同时使用内存缓存(如 LruCache),需要额外处理数据一致性,否则容易导致缓存过期数据不一致问题。