检测 OkHttp 响应是否来自缓存(使用 Retrofit)

Detect if OkHttp response comes from cache (with Retrofit)

有没有办法检测 Retrofit 响应是来自配置的 OkHttp 缓存还是实时响应?

客户定义:

Cache cache = new Cache(getCacheDirectory(context), 1024 * 1024 * 10);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .cache(cache)
            .build();

Api定义:

@GET("/object")
Observable<Result<SomeObject>> getSomeObject();

调用示例:

RetroApi retroApi = new Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl(baseUrl)
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(RetroApi.class);

result = retroApi.getSomeObject().subscribe((Result<SomeObject> someObjectResult) -> {
     isFromCache(someObjectResult); // ???
});

解决方案一:

通过使用 okhttp3.Response.cacheResponse() 您可以检查是否从缓存中收到了响应:

Returns the raw response received from the cache. Will be null if this response didn't use the cache

要从改造响应对象获取原始 OkHttp 响应,请使用 .raw()。即:

public boolean isFromCache(Result<?> retroResult) {
    return retroResult.response().raw().cacheResponse() != null;
}

解决方案二:

您可以通过 new OkHttpClient.Builder().addInterceptor(...) 向 OkHttp 客户端添加拦截器。 在此拦截器中,通过 chain.proceed(chain.request) 检索响应。 然后分别用Response.cacheResponse()Response.networkResponse()判断响应来自哪里

完整代码:

new OkHttpClient.Builder().addInterceptor(chain -> {
        Response response = chain.proceed(chain.request());
        if (response.cacheControl() != null) {
            // from cache
        } else if (response.networkResponse() != null) {
            // from network
        }
        return response;
});

任何时候你有 okhttp3.Response (retrofit2.Response.raw()),你可以检查响应是否来自缓存。

引用杰西·威尔逊的话:

There are a few combos.

.networkResponse() only – your request was served from network exclusively.

.cacheResponse() only – your request was served from cache exclusively.

.networkResponse() and .cacheResponse() – your request was a conditional GET, so headers are from the network and body is from the cache.

因此,对于您的示例,isFromCache 方法如下所示:

boolean isFromCache(Result<?> result) {
  return result.response().raw().networkResponse() == null;
}
if (response.raw().cacheResponse() != null) {  
    // true: response was served from cache
}

if (response.raw().networkResponse() != null) {  
    // true: response was served from network/server
}

但我建议您检查您的 networkResponse 是否为 null,因为一旦请求被缓存并且您也有 networkResponse,您的响应通过流向下传递是来自网络而不是缓存(取决于 maxStale 你可能已在 CacheControl 上指定)。因此,请检查 networkResponse 是否不为空,如果为空且您的 cacheResponse 不为空,则响应从缓存提供,反之亦然。

为时已晚,但也许这对某人有帮助。当内容来自网络时:response.networkResponse()response.cacheResponse()不为空,当内容来自缓存时response.networkResponse()为空,所以来自@sebastian的代码:

new OkHttpClient.Builder().addInterceptor(chain -> {
        Response response = chain.proceed(chain.request());
        if (response.cacheControl() != null) {
            // from cache
        } else if (response.networkResponse() != null) {
            // from network
        }
        return response;
});

只需替换为此代码:

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder.addInterceptor(new Interceptor() {
    @NonNull
    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        if (response.networkResponse() != null) {
            Log.v(TAG, "Response from networkResponse(): " + response.networkResponse());
        } else if (response.cacheResponse() != null) {
            Log.v(TAG, "Response from cacheControl(): " + response.cacheResponse());
        }
        return response;
    }
});

如果 cacheResponse 存在,则响应来自缓存 AND networkResponse(如果存在)的响应代码为 304。参见:https://github.com/square/okhttp/issues/4539