4、开启OkHttp缓存:配置Cache对象(目录、大小)

缓存这玩意儿,说白了一个字——省。省流量、省时间、省服务器压力。我在做一款新闻资讯App时,发现每次下拉刷新都要重新请求图片和JSON,用户体验卡得要命。后来加了缓存,页面秒开,连后台都跑来问「你是不是偷偷做了预加载?」

OkHttp的缓存机制不复杂,核心就两步:造一个Cache对象,然后塞给OkHttpClient。但这里面的坑,嗯,比想象中多。咱们一个一个来。

4.1 Cache对象:目录与大小

Cache构造函数只有两个参数:

Cache(File directory, long maxSize);

directory:缓存文件存放的目录。你必须确保这个目录存在,而且App有读写权限。
maxSize:缓存的最大字节数。超过这个值,OkHttp会按照LRU策略自动清理旧缓存。

我个人习惯把缓存目录放在 context.getCacheDir() 下,比如:

File cacheDir = new File(context.getCacheDir(), "okhttp_cache");
if (!cacheDir.exists()) {
    cacheDir.mkdirs();
}
Cache cache = new Cache(cacheDir, 10 * 1024 * 1024); // 10MB
⚠️ 我曾经踩过的坑:
有一次直接用了 getExternalCacheDir(),结果用户一插USB传输文件,缓存路径就变了,App直接崩溃。所以对内存储路径,我建议统一用 getCacheDir()——它不会随SD卡挂载状态变化,安全又稳妥。

4.2 缓存大小怎么选?

这个问题没有标准答案,但我有几个经验值供你参考:

场景 推荐缓存大小 原因
新闻/图文类App 10MB – 50MB 图片+文章JSON,一天量大概5-15MB
视频类App(缩略图) 20MB – 100MB 封面图多,但视频本身不缓存
工具类/API查询 1MB – 5MB 只缓存接口响应,数据量小
低端机/存储敏感 不超过 getCacheQuota() 避免撑爆用户存储
💡 小技巧:
你可以用 StorageStatsManager 动态获取可用空间,然后按比例分配。比如取剩余空间的2%作为缓存上限,这样既不会浪费也不会爆。

4.3 为OkHttpClient设置缓存

拿到Cache对象后,直接在Builder上调用 cache() 即可:

OkHttpClient client = new OkHttpClient.Builder()
        .cache(cache)
        .build();

就这么简单?对。但真正让缓存「动起来」的是HTTP响应头里的 Cache-Control。OkHttp只负责存储,怎么缓存、缓存多久,那是服务器和你自己决定的。关于Cache-Control的详细配置,咱们下一章再展开,这里先做个铺垫。

📌 关键点回顾:
- 目录必须保证存在且有写权限。
- 大小建议根据业务动态调整,不用死磕固定值。
- 缓存只存储符合缓存条件的响应(即服务器返回了Cache-Control或Expires等)。

4.4 验证缓存是否生效

配置完心里没底?我一般会抓包看两个地方:

  • 第一次请求:返回200,响应头带 Cache-Control: public, max-age=3600
  • 第二次请求(一小时之内):返回 200 (disk cache),或者状态码 304 Not Modified

你可以在OkHttp的日志拦截器里打印缓存命中情况,或者直接看响应头的 Age、X-Cache 字段。没有日志?没关系,可以加一个自定义拦截器手动打印:

class CacheLogInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        Response.CacheControl cacheControl = response.cacheControl();
        Log.d("OkHttp", "URL: " + request.url() 
                + "  Cache: " + (response.cacheResponse() != null ? "HIT" : "MISS")
                + "  Age: " + response.header("Age"));
        return response;
    }
}

你想想看,有了这个日志,谁还敢说缓存没生效?

4.5 整个配置流程总览

下面这张图把从目录创建到缓存生效的完整路径串了起来,建议你保存下来,以后配置时拿出来看一眼:

OkHttp 缓存配置流程 1. 创建缓存目录 getCacheDir() / 存在检查 2. new Cache(目录, 大小) maxSize 建议按场景动态调整 3. .cache(cache) OkHttpClient.Builder 4. .build() 构建 client 得到带缓存的 OkHttpClient 5. 发起网络请求 OkHttp 自动判缓存策略 命中 / 未命中 💡 提示:缓存实际生效还需服务器配合 Cache-Control 响应头,详见下一章。

4.6 善后与监控

缓存配置好了,别忘了偶尔看看它是不是正常工作。我习惯在App的开发者菜单里加一个「清除缓存」的按钮,方便测试:

// 清理整个缓存目录
client.cache().evictAll();

或者只想清理某条URL的缓存:

// 需要知道完整的请求对象
Request request = new Request.Builder().url("https://api.example.com/data").build();
cache.remove(request);
💡 监控指标:
你可以通过 cache.flush() 后的文件列表,或者 cache.size() 来获取当前占用空间。如果发现缓存增长异常(比如文件数量暴涨),多半是某些响应没有Cache-Control导致每次都生成新文件——这时候该检查服务端的配置了。