You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改Retrofit/OkHttp缓存满时的清理逻辑?

问题:自定义OkHttp缓存满时的淘汰策略

默认情况下,OkHttp(Retrofit基于OkHttp实现缓存)在缓存满时,会优先删除更早过期的缓存响应:

  • 示例1:1MB空缓存中先存入700KB、1小时后过期(Cache-Control: max-age=3600)的响应,再存入800KB、30分钟后过期的响应,后者无法被缓存。
  • 示例2:1MB缓存中有800KB、1小时后过期的响应,存入800KB、5小时后过期的响应时,前者会被删除,后者被存储。

需求是修改为基于**接收时间(OkHttp-Received-Millis响应头)**删除缓存,即最早接收的响应优先被淘汰。

当前的Retrofit/OkHttp初始化代码:

private Retrofit retrofit(String baseUrl) {
        return new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(cachingClient(CACHE_IDENTIFIER))
                .addConverterFactory(GsonConverterFactory.create(gson()))
                .build();
}

private OkHttpClient cachingClient(String identifier) {
        return new OkHttpClient.Builder()
                .cache(cache(identifier))
                .addInterceptor(InterceptorsKt.authorizationInterceptor(Credentials.basic(USER, PASSWD)))
                .build();
}

private Cache cache(String identifier) {
        return new Cache(new File(context.get().getCacheDir(), identifier), CACHE_SIZE);
}

缓存响应结构示例:

http://example.com:8754/random/hello
GET
0
HTTP/1.1 200 
11
Cache-Control: max-age=2592000, immutable
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Wed, 21 Sep 2022 08:01:31 GMT
Keep-Alive: timeout=60
Connection: keep-alive
OkHttp-Sent-Millis: 1663747322078
OkHttp-Received-Millis: 1663747322254

解决方案

要实现基于接收时间的缓存淘汰,需要自定义Cache类,重写其缓存裁剪逻辑(trimToSize方法),替换默认的过期时间排序为OkHttp-Received-Millis排序。

1. 自定义Cache类

继承OkHttp的Cache类,重写trimToSize方法,修改缓存entry的排序规则:

import okhttp3.Cache;
import okhttp3.Headers;
import okhttp3.internal.cache.DiskLruCache;
import okhttp3.internal.cache.DiskLruCache.Entry;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ReceivedTimeBasedCache extends Cache {
    public ReceivedTimeBasedCache(File directory, long maxSize) {
        super(directory, maxSize);
    }

    @Override
    public synchronized void trimToSize(long maxSize) throws IOException {
        DiskLruCache cache = this.cache;
        if (cache == null) return;

        long currentSize = cache.size();
        if (currentSize <= maxSize) return;

        // 收集所有缓存条目
        List<Entry> entries = new ArrayList<>();
        for (String key : cache.keys()) {
            Entry entry = cache.get(key);
            if (entry != null) {
                entries.add(entry);
            }
        }

        // 按OkHttp-Received-Millis从小到大排序,最早接收的先删除
        Collections.sort(entries, (a, b) -> {
            try {
                Headers aHeaders = Headers.of(a.responseHeaders);
                Headers bHeaders = Headers.of(b.responseHeaders);

                long aReceivedTime = Long.parseLong(aHeaders.get("OkHttp-Received-Millis"));
                long bReceivedTime = Long.parseLong(bHeaders.get("OkHttp-Received-Millis"));

                return Long.compare(aReceivedTime, bReceivedTime);
            } catch (NumberFormatException | NullPointerException e) {
                // 解析失败时 fallback 到默认的过期时间排序
                return Long.compare(a.expireAt, b.expireAt);
            }
        });

        // 删除条目直到缓存大小符合要求
        long bytesToRemove = currentSize - maxSize;
        for (Entry entry : entries) {
            if (bytesToRemove <= 0) break;
            cache.remove(entry.key);
            bytesToRemove -= entry.size;
        }
    }
}

2. 替换原Cache初始化

修改cache方法,使用自定义的ReceivedTimeBasedCache:

private Cache cache(String identifier) {
        return new ReceivedTimeBasedCache(new File(context.get().getCacheDir(), identifier), CACHE_SIZE);
}

注意事项

  • 该实现依赖OkHttp内部的DiskLruCache和Entry类(属于okhttp3.internal包),这些是内部API,不同OkHttp版本可能存在兼容性问题,升级时需要验证代码。
  • 当OkHttp-Received-Millis头解析失败时,会自动 fallback 到默认的过期时间排序,避免缓存裁剪逻辑异常。
  • 重写的trimToSize方法保持了父类的synchronized锁逻辑,确保线程安全。

内容的提问来源于stack exchange,提问作者MarekChr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 01:55:32