缓存基础:为什么我这么看重它?
做 Android 网络层好些年了,我越发觉得——缓存不是锦上添花,而是核心性能的底线。今天开篇第一课,我们就来聊聊 HTTP 缓存最基础的东西,以及它跟 Retrofit 到底有什么关系。
什么是 HTTP 缓存?
说白了,HTTP 缓存就是客户端把服务器返回的响应「暂存」一份。下次再请求同一个资源时,如果条件允许,直接用本地的副本,不再跑一趟网络。
核心思想:拿空间换时间,用存储开销降低网络延迟。
但这不意味着「所有响应都能缓存」。服务器会在响应头里明确告诉客户端:
Cache-Control: max-age=3600 —— 这个资源 1 小时内有效。
ETag: "abc123" —— 资源版本标识,用来做条件请求。
我遇到过不少开发以为「加了缓存接口就快」,结果乱配过期时间,导致用户看到旧数据。嗯,这里要特别注意:缓存协议是服务器控制的,客户端只是遵守者。
缓存与网络请求的关系
整个过程可以拆成三步:
- 判断缓存是否存在:如果本地没有缓存,直接发起网络请求。
- 判断缓存是否新鲜:根据
max-age或Expires判断。如果新鲜,直接使用缓存,零网络开销。 - 缓存过期了怎么办?:发起条件请求(带上
If-None-Match或If-Modified-Since)。服务器比对后,如果资源没变,返回304 Not Modified,客户端刷新缓存有效期;如果变了,返回 200 和新数据。
你想想看,最理想的情况是「缓存命中」——直接本地读,延迟从几百毫秒降到几毫秒。最差的情况是「缓存未命中」且服务器数据变了——完整走一次网络,但也能顺便更新缓存。真正让人头疼的是「缓存过期但资源没变却走全量请求」——浪费带宽,还慢。
我的习惯:在设计接口时,尽量让服务器返回 ETag。因为即使 max-age 设得很短,条件请求的 304 响应体为空,流量几乎为零。
为什么缓存对 Retrofit 至关重要?
Retrofit 本身不处理缓存。它只是一个声明式 HTTP 接口封装,真正干活的是底层的 OkHttp。而 OkHttp 内置了一套完整的 HTTP 缓存实现——基于 Cache 类和 CacheStrategy。
我记得有一次线上反馈:某列表页加载总是白屏几秒钟。查了日志发现,用户每次进入页面都发起了完整网络请求,接口数据近一周都没变过。加上一天有效期的缓存后,冷启动加载时间从 3s 降到了 300ms。这就是缓存的力量。
给 Retrofit 配置缓存,本质上就是给 OkHttp 的 OkHttpClient 加一个 Cache 对象:
// 创建缓存目录
File cacheDir = new File(context.getCacheDir(), "http_cache");
// 缓存大小:10MB
Cache cache = new Cache(cacheDir, 10 * 1024 * 1024);
// 配置OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(new MyCacheInterceptor()) // 可选的自定义拦截器
.build();
// 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client)
.build();
就这么简单,Retrofit 的所有请求就自动走缓存逻辑了。但说实话,90% 的问题都不在配置,而在 服务器的缓存响应头没配对,或者客户端没理解对。
缓存命中率的维度
我们通常用以下几个指标衡量:
| 指标 | 含义 | 理想值 |
|---|---|---|
| 缓存命中率(Hit Rate) | 请求中直接使用缓存的比例 | > 60% |
| 字节命中率(Byte Hit Rate) | 从缓存获取的字节占总字节的比例 | > 80% |
| 条件请求成功率(304 Rate) | 发起条件请求后返回 304 的比例 | > 90% |
我曾经接手过一个项目,缓存命中率不足 10%。排查发现:所有接口都没配 Cache-Control,OkHttp 默认对无缓存的响应直接不缓存。加了响应头后,命中率飙到 70%。你看,一个小头字段差这么远。
一张图看懂本章知识体系
下面这张图总结了 HTTP 缓存在 Retrofit 链路中的完整流程,以及影响命中率的关键节点。
常见误区与避坑
我曾经踩过的坑:
- 误以为添加了
Cache对象就万事大吉,结果接口返回了Cache-Control: no-store,根本存不下来。 - 在调试模式下频繁清理缓存,导致「命中率」数据失真。生产环境一定要用真实的用户行为数据。
- 忘记给 OkHttp 配
cache对象——Retrofit 不会自动帮你加,必须手工配置。
我的建议:在开发阶段,先让后端同事确认每个接口的缓存策略。推荐使用 Cache-Control: public, max-age=86400 这类常见配置,配合 ETag 做验证。初期宁可保守一点,设长过期时间,后续再根据业务调整。
好了,这一章我们把缓存的基础概念、与网络请求的配合方式、以及 Retrofit 里怎么搭架子都走了一遍。记住一句话:缓存不是玄学,是工程。后面我会带你实战提升命中率,从拦截器到策略定制,一步步攻破。