在Android中为OkHttp设置缓存的正确方法

 纽约纽约MrWaNg 发布于 2022-12-29 10:08

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

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

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

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向服务器发出的一个请求,它实际上没有使用缓存:

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,就好像它没有正确保存到最终文件一样,就像我提出的其他要求一样.

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

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将其存储在缓存中的方式:

{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中看到这些消息.

我究竟做错了什么?

更新:

现在这是Jesse建议后的服务器响应:

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输出中得到的:

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

1 个回答
  • 我刚刚解决了这个问题.当我尝试从源代码使用OkHttp时,缓存测试失败的情况有点误导.

    问题很简单,其他的请求方法是在响应中获取一个主体,并且最终没有关闭.这就解释了为什么我在缓存中看到了".tmp"文件,但仍然令人困惑和误导,因为这个请求方法正在消耗并从响应中关闭正文.它就像缓存编辑器的锁或监视器对所有请求都是全局的,而不是按请求.我虽然不是在我阅读代码时,但是当它使用哈希作为密钥时请求.

    无论如何,就是这样:D

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

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

    ...在处理响应代码的每个案例之前.这样我就不会错过对响应机构的密切联系.

    2022-12-29 10:11 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有