Retrofit 2 缓存 - 拦截器链

Retrofit 2 caching - chain of interceptors

我需要实现 API 响应的基本缓存。我做了一个小游乐场项目,调用 GitHub API 并且缓存成功(我用 Charles 来验证)。但是,当我将此解决方案转移到我的目标项目时,缓存不再起作用。链中的多个拦截器可能是原因吗?

来自 playground 项目的代码(有效):

拦截器(目标项目相同):

public class CacheControlInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        return response.newBuilder()
                .header("Cache-Control", "only-if-cached")
                .build();
    }
}

缓存和客户端声明:

long SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MB
final Cache cache = new Cache(new File(getCacheDir(), "retrofit_cache"), SIZE_OF_CACHE);

OkHttpClient.Builder client = new OkHttpClient.Builder().cache(cache);
client.networkInterceptors().add(new CacheControlInterceptor());

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/users/")
        .addConverterFactory(GsonConverterFactory.create())
        .client(client.build())
        .build();

CacheControlInterceptor 的调试屏幕: screen


来自目标项目的代码(不工作):

缓存和客户端声明:

private OkHttpClient provideOkHttpClient() {
    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder();
    okhttpClientBuilder.connectTimeout(30, TimeUnit.SECONDS);
    okhttpClientBuilder.readTimeout(30, TimeUnit.SECONDS);
    okhttpClientBuilder.writeTimeout(30, TimeUnit.SECONDS);

    okhttpClientBuilder.addInterceptor(loggingInterceptor);
    okhttpClientBuilder.addInterceptor(new JwtRenewInterceptor(getUserSession()));
    okhttpClientBuilder.addInterceptor(new AutoLoginInterceptor(getUserSession()));
    okhttpClientBuilder.addNetworkInterceptor(new CacheControlInterceptor());

    long SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MB
    final Cache cache = new Cache(new File(getCacheDir(), "retrofit_cache"), SIZE_OF_CACHE);
    okhttpClientBuilder.cache(cache);

    return okhttpClientBuilder.build();
}

CacheControlInterceptor 调试屏幕:screen

如果您想将一些 header 应用于所有使用 OkHttp 缓存的请求,您应该使用应用程序拦截器,而不是网络拦截器。否则,您不会为缓存机制提供 return 缓存响应的机会。 它在 OkHttp wiki 上有很好的说明

所以很可能在您的代码中发生的事情是您让 Cache 存储响应,但您从不使用它们,因为去往缓存的请求丢失 only-if-cached header。

试试看 okhttpClientBuilder.addInterceptor(new CacheControlInterceptor());

其实这个错误是我对 http headers 的推理不当造成的。我认为方法 addHeaderheader 只会添加键 Cache-Control,然后添加值 only-if-cached。然而,它只会增加价值!由于在我的目标项目的 API 中没有 header 键 Cache-Control(与 GitHub API 中不同)因此没有值 only-if-cached 的位置被存储。