在Android中为OkHttp设置缓存的正确方法 [英] Correct way of setting up cache for OkHttp in Android

查看:322
本文介绍了在Android中为OkHttp设置缓存的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为OkHttp设置缓存,因此它仅在我第一次尝试从服务器检索响应时才请求服务器,直到标头日期或来自服务器的缓存控制标头过期为止使来自缓存的响应无效.

I'm trying to set up the cache for OkHttp, so it only requests to the server the first time I try to retrieve a response from the server until the expires header date, or cache-control header that comes from the server invalidates the response from the cache.

当前,它缓存响应,但是在再次请求资源时不使用它.可能这不是应该使用的方式.

Currently, its caching the response, but not using it when requesting the resource again. May be this is not the way it was supposed to be used.

我正在使用这样的缓存设置OkHttpClient:

I'm setting up OkHttpClient with cache like this:

public static Cache createHttpClientCache(Context context) {
    try {
        File cacheDir = context.getDir("service_api_cache", Context.MODE_PRIVATE);
        return new Cache(cacheDir, HTTP_CACHE_SIZE); 
    } catch (IOException e) {
        Log.e(TAG, "Couldn't create http cache because of IO problem.", e);
        return null;
    }
}

这是这样使用的:

if(cache == null) {
    cache = createHttpClientCache(context);
}
sClient.setCache(cache);

这是我使用OkHttp向服务器发出的请求之一,而该请求实际上无法使用缓存:

This is how I make one of the requests to the server with OkHttp that's actually failing to use the cache:

public static JSONObject getApi(Context context) 
        throws IOException, JSONException, InvalidCookie {

    HttpCookie sessionCookie = getServerSession(context);
    if(sessionCookie == null){
        throw new InvalidCookie();
    }
    String cookieStr = sessionCookie.getName()+"="+sessionCookie.getValue();

    Request request = new Request.Builder()
        .url(sServiceRootUrl + "/api/"+API_VERSION)
        .header("Accept", "application/json")
        .header("Cookie", cookieStr)
        .build();

    Response response = sClient.newCall(request).execute();
    if(response.code() == 200){
        String charset = getResponseCharset(response);
        if(charset == null){
            charset = "utf-8";
        }
        String responseStr = new String(response.body().bytes(), charset);
        response.body().close();
        return new JSONObject(responseStr);
    } else if(response.code() == 401){
        throw new InvalidCookie();
    } else {
        return null;
    }

}

如果到达我指定为OkHttp的缓存的目录,则可以看到日志文件和其他4个文件,其中包含一些请求的响应.该请求(我刚刚粘贴了代码的/api)存储在缓存目录中,因此它确实被缓存了,但是文件名的末尾带有.tmp,就像没有正确保存到最终文件一样,就像我提出的其他要求一样.

If I get to the directory I specified to be the cache for OkHttp I can see the journal file and 4 other files that contains the responses for some of the requests. This request (the /api one I've just pasted the code) is stored on the cache directory so it was really cached, but the filename has a .tmp at the end, like if it wasn't properly saved to the final file, like other request I made.

这是请求的服务器响应标头的样子:

This is how it looks like the headers of the server response for the request:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Expires: Sat, 09 Aug 2014 19:36:08 GMT
Cache-Control: max-age=86400, must-revalidate
Last-Modified: Sun, 04 Aug 2013 15:56:04 GMT
Content-Length: 281
Date: Fri, 08 Aug 2014 19:36:08 GMT

这是OkHttp将其存储在缓存中的方式:

And this is how OkHttp stores it on the cache:

{HOST}/api/0.3
GET
0
HTTP/1.1 200 OK
9
Server: Apache-Coyote/1.1
Expires: Sat, 09 Aug 2014 19:36:08 GMT
Cache-Control: max-age=86400, must-revalidate
Last-Modified: Sun, 04 Aug 2013 15:56:04 GMT
Content-Length: 281
Date: Fri, 08 Aug 2014 19:36:08 GMT
OkHttp-Selected-Protocol: http/1.1
OkHttp-Sent-Millis: 1407526495630
OkHttp-Received-Millis: 1407526495721

OkHttp创建此文件后,它将继续向服务器请求相同的资源.我可以在Wireshark中看到这些消息.

After OkHttp creates this file, it keeps requesting to the server the same resource. I can see those messages in Wireshark.

我在做什么错了?

更新:

这是杰西建议之后的服务器响应:

This is now the server response after Jesse suggestion:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Expires: Thu, 14 Aug 2014 18:06:05 GMT
Last-Modified: Sun, 10 Aug 2014 12:37:06 GMT
Content-Length: 281
Date: Wed, 13 Aug 2014 18:06:05 GMT

更新2: 尝试了代码版本,发现很有可能是有关缓存的某个错误.这是我从Maven输出中得到的:

UPDATE 2: Tried the code version and found out that it is quite probable there is a bug somewhere regarding the cache. This is what I've got from the Maven output:

Results :

Failed tests: 
  CacheTest.conditionalHitUpdatesCache:1653 expected:<[A]> but was:<[B]>

Tests in error: 
  CallTest.tearDown:86 » IO failed to delete file: C:\Users\Adrian\AppData\Local...

Tests run: 825, Failures: 1, Errors: 1, Skipped: 17

可以在此处看到更完整的日志: https://gist.github.com/16BITBoy/344ea4c22b543f397f53

A more complete log can be seen here: https://gist.github.com/16BITBoy/344ea4c22b543f397f53

推荐答案

我刚刚解决了这个问题.当我尝试从源代码中使用OkHttp时,缓存测试失败了,这在某种程度上具有误导性.

I just solved the problem. It was somewhat misleading that the cache tests where failing when I tried to use OkHttp from source.

问题很容易,原因是其他请求方法在响应中包含主体,并且最终没有关闭.这就解释了为什么我在高速缓存中看到了".tmp"文件,但由于该请求方法正在消耗响应并关闭响应正文,因此仍然令人困惑和误解.就像高速缓存编辑器的锁或监视器一样,它对所有请求都是全局的,而不是按请求.我不是在阅读代码时,而是在请求中使用哈希作为键的时候.

The problem was quite easy and it was that other of the request methods was getting a body on the response, and it wasn't closed at the end. That explains why I saw the ".tmp" file in the cache, but still a confusing and misleading because of the fact that this request method was consuming and closing the body from the response. Its like the lock or monitor for the cache editor is global for all requests, instead of being by request. I though it wasn't when I read the code, when it used a hash for the request as a key.

反正就是:D

从现在开始,我将尝试坚持这样的模式...

From now on, I'll try to stick to a pattern like this...

String respBody = null;
if(response.body() != null) {
    respBody = response.body().string();
    response.body().close();
}

...在处理每种情况下的响应代码之前.这样,我就不会错过对响应正文的直接呼叫.

...before handling each case for response code. That way I won't be missing a close call to the response body.

这篇关于在Android中为OkHttp设置缓存的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆