Retrofit + OkHTTP - 响应缓存无法正常工作

dae*_*tus 4 android caching cache-control retrofit okhttp

我知道有很多类似的问题,但我已经阅读了所有这些问题,但没有一个真正有用.

所以,这是我的问题:

我正在使用retrofit + okhttp从API获取一些数据,我想缓存它们.不幸的是,我没有API服务器的管理员权限,因此我无法修改服务器返回的标头.(目前,服务器返回Cache-control:private)

所以我决定使用okhttp标头欺骗来插入适当的缓存标头.可悲的是,无论我做什么,缓存似乎都不起作用.

我初始化这样的api服务:

int cacheSize = 10 * 1024 * 1024; // 10 MiB
File cacheFile = new File(context.getCacheDir(), "thumbs");
final Cache cache = new Cache(cacheFile, cacheSize);

OkHttpClient client = new OkHttpClient();
client.setCache(cache);
client.interceptors().add(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());
        return originalResponse.newBuilder()
                .removeHeader("Access-Control-Allow-Origin")
                .removeHeader("Vary")
                .removeHeader("Age")
                .removeHeader("Via")
                .removeHeader("C3-Request")
                .removeHeader("C3-Domain")
                .removeHeader("C3-Date")
                .removeHeader("C3-Hostname")
                .removeHeader("C3-Cache-Control")
                .removeHeader("X-Varnish-back")
                .removeHeader("X-Varnish")
                .removeHeader("X-Cache")
                .removeHeader("X-Cache-Hit")
                .removeHeader("X-Varnish-front")
                .removeHeader("Connection")
                .removeHeader("Accept-Ranges")
                .removeHeader("Transfer-Encoding")
                .header("Cache-Control", "public, max-age=60")
              //.header("Expires", "Mon, 27 Apr 2015 08:15:14 GMT")
                .build();
    }
});

RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint(API_ROOT)
    .setLogLevel(RestAdapter.LogLevel.HEADERS_AND_ARGS)
    .setClient(new OkClient(client))
    .setConverter(new SimpleXMLConverter(false))
    .setRequestInterceptor(new RequestInterceptor() {
        @Override
        public void intercept(RequestFacade request) {
            if (Network.isConnected(context)) {
                int maxAge = 60; // read from cache for 2 minutes
                request.addHeader("Cache-Control", "public, max-age=" + maxAge);
            } else {
                int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
                request.addHeader("Cache-Control",
                    "public, only-if-cached, max-stale=" + maxStale);
            }
        }
    })
    .build();
api = restAdapter.create(ApiService.class);
Run Code Online (Sandbox Code Playgroud)

当然,没有必要删除所有这些标头,但我想尽可能地使响应干净,以排除来自这些额外标头的一些干扰.

正如你所看到的,我试图欺骗Expires和Date标题(我尝试删除它们,设置它们以便它们之间存在完全最大年龄差异,并将Expires设置为将来).我还尝试了各种Cache-control值,但没有运气.

我确保cacheFile存在,isDirectory并且可由应用程序写入.

这些是通过改造直接记录的请求和响应标头:

Request:
Cache-Control: public, max-age=60
---> END HTTP (no body)

Response:
Date: Mon, 27 Apr 2015 08:41:10 GMT
Server: Apache/2.2.22 (Ubuntu)
Expires: Mon, 27 Apr 2015 08:46:10 GMT
Content-Type: text/xml; charset=UTF-8
OkHttp-Selected-Protocol: http/1.1
OkHttp-Sent-Millis: 1430124070000
OkHttp-Received-Millis: 1430124070040
Cache-Control: public, max-age=60
<--- END HTTP (-1-byte body)
<--- BODY: ...
Run Code Online (Sandbox Code Playgroud)

而且,最后一个奇怪的事件:在某些时候,缓存工作了几分钟.我得到了合理的命中数,甚至是离线请求返回缓存值.(它发生在使用此处发布的确切设置时)但是当我重新启动应用程序时,一切都恢复到"正常"(常量命中计数0).

如果有人知道这里可能出现什么问题,我会很高兴得到任何帮助:)

Bre*_*ein 8

使用networkInterceptors()而不是拦截器().这与您删除与缓存有些相关的任何标头的策略相结合将起作用.这是简短的答案.

当您使用拦截器更改标头时,它不会在调用CacheStrategy.isCacheable()之前进行任何调整.值得查看CacheStrategy和CacheControl类,以了解OKHttp如何处理与缓存相关的标头.在http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html上进行ctrl + f"缓存"也是值得的.

我不确定networkInterceptors()和interceptors()文档是否不清楚或是否存在错误.一旦我再研究一下,我会更新这个答案.


Paw*_*ari 7

还有一件事要补充一点,除了Brendan Weinstein的回答只是为了确认OkHttp3缓存不适用于帖子请求.