OkHttp是否支持并行管理多个缓存目录及实现方法
OkHttp多缓存目录并行管理实现方案
原生OkHttp不提供开箱即用的多缓存目录并行管理能力,内置Cache实现仅支持绑定单个文件目录、配套单一淘汰策略,无法直接配置多目录+独立淘汰规则的层级缓存逻辑,但可以通过自定义拦截器组合多个独立Cache实例的方式,完全覆盖你描述的业务场景,无需修改OkHttp源码。
核心实现逻辑
- 不给OkHttpClient设置全局默认Cache实例,所有缓存读写逻辑全部通过自定义拦截器实现,避免内置缓存逻辑干扰
- 分别初始化两个独立的
Cache实例,分别绑定cacheDir1、cacheDir2,各自配置对应的evictionPolicy1、evictionPolicy2淘汰策略,两个实例的缓存计算、淘汰操作完全隔离 - 严格按照业务要求的优先级,在拦截器内手动实现缓存命中判断、内容读取、新响应落盘的流程:优先查
cacheDir2对应的缓存,再查cacheDir1对应的缓存,均未命中时走网络请求,普通浏览的响应写入cacheDir1,用户主动保存的响应写入cacheDir2
具体实现步骤
1. 初始化两个独立的缓存实例
两个实例绑定完全独立的文件目录,分别配置对应淘汰策略,互不干扰:
import okhttp3.Cache import java.io.File // 普通浏览缓存:绑定cacheDir1,使用evictionPolicy1淘汰策略 val browseCache = Cache( directory = File(cacheDir1Path), maxSize = 100L * 1024 * 1024, // 按业务设置缓存大小上限 // 自定义evictionPolicy1逻辑,默认LRU可直接使用内置实现,特殊策略重写Cache的淘汰相关方法即可 ) // 用户主动保存的缓存:绑定cacheDir2,使用evictionPolicy2淘汰策略 val savedCache = Cache( directory = File(cacheDir2Path), maxSize = 500L * 1024 * 1024, // 按业务设置缓存大小上限 // 自定义evictionPolicy2逻辑,和browseCache的策略完全独立 )
2. 实现自定义多缓存拦截器
拦截器是整个逻辑的核心,严格按照优先级处理缓存查找和写入:
import okhttp3.Interceptor import okhttp3.Response class MultiCacheInterceptor( private val savedCache: Cache, private val browseCache: Cache ) : Interceptor { // 标记类:用于标识当前请求是用户主动触发的保存类请求 class SaveRequestMarker override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() // 第一步:优先查找用户主动保存的cache2,命中直接返回 savedCache.get(request)?.let { cachedResponse -> return cachedResponse.newBuilder() .cacheResponse(cachedResponse) .build() } // 第二步:cache2未命中,查找普通浏览的cache1,命中直接返回 browseCache.get(request)?.let { cachedResponse -> return cachedResponse.newBuilder() .cacheResponse(cachedResponse) .build() } // 第三步:两个缓存均未命中,放行请求走网络获取真实响应 val networkResponse = chain.proceed(request) // 第四步:根据请求标记判断写入对应缓存 val isUserSaveRequest = request.tag(SaveRequestMarker::class.java) != null val cacheableResponse = networkResponse.newBuilder().build() return if (isUserSaveRequest) { savedCache.put(cacheableResponse) cacheableResponse } else { browseCache.put(cacheableResponse) cacheableResponse } } }
3. 配置OkHttpClient
注意不要调用cache()方法设置全局缓存,避免和自定义拦截器的逻辑冲突:
import okhttp3.OkHttpClient val okHttpClient = OkHttpClient.Builder() // 注册自定义多缓存拦截器 .addInterceptor(MultiCacheInterceptor(savedCache, browseCache)) // 其余业务配置(超时、证书、其他拦截器等)按需求添加 .build()
4. 业务侧调用方式
- 普通浏览URL:直接构造常规Request发起请求即可,响应会自动写入
cacheDir1 - 用户主动保存指定URL:构造Request时加上对应标记,响应会自动写入
cacheDir2
// 用户主动保存URL的请求构造示例 val saveRequest = Request.Builder() .url(targetUrl) .tag(MultiCacheInterceptor.SaveRequestMarker::class.java, MultiCacheInterceptor.SaveRequestMarker()) .build() okHttpClient.newCall(saveRequest).enqueue(/* 回调逻辑 */)
注意事项
- 两个缓存实例绑定的目录必须完全独立,不能存在文件交叉,否则会出现缓存损坏、淘汰逻辑互相干扰的问题
- 缓存有效期、是否允许缓存的判断规则,可以根据业务需求在拦截器内扩展,既可以遵循标准HTTP缓存头规则,也可以自定义本地过期时间
- 自定义淘汰策略时,仅需重写对应Cache实例的内部淘汰计算方法,两个实例的统计、淘汰操作完全隔离,不会互相影响
内容的提问来源于stack exchange,提问作者avianey
相关产品推荐
相关产品推荐

