Правильный способ настройки кеша для OkHttp в Android
Я пытаюсь настроить кэш для 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.
Что я делаю неправильно?
ОБНОВИТЬ:
Теперь это ответ сервера после предложения Джесси:
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
3 ответа
Я только что решил проблему. Это несколько вводило в заблуждение, что тестирование кеша проваливалось, когда я пытался использовать OkHttp из исходного кода.
Проблема была довольно простой, и было то, что другие методы запроса получали тело ответа, и в конце оно не было закрыто. Это объясняет, почему я увидел файл ".tmp" в кэше, но все еще сбивает с толку и вводит в заблуждение из-за того, что этот метод запроса потреблял и закрывал тело из ответа. Это как блокировка или монитор для редактора кеша, глобальная для всех запросов, а не по запросу. Я думал, что это было не так, когда я читал код, когда он использовал хеш для запроса в качестве ключа.
Во всяком случае, так и было:D
С этого момента я постараюсь придерживаться такой картины...
String respBody = null;
if(response.body() != null) {
respBody = response.body().string();
response.body().close();
}
... перед обработкой каждого случая для кода ответа. Таким образом, я не пропущу внимательный звонок к телу ответа.
У меня та же проблема, затем я отлаживаю исходный код okhttp, вы можете посмотреть на CacheStrategy.cacheResponseAge (), okhttp будет использовать nowMillis и serveDate, serveDate получить от http-заголовка вашего сервера "Date", nowMillis получить с вашего устройства Android. Поэтому, когда время сервера превышает время устройства, okhttp не будет получать из кэша, если max-age мало.
прости мой бедный английский ^_^
Ваш сервер принудительно проверяет кеш с помощью этого заголовка ответа:
Cache-Control: max-age=86400, must-revalidate
Удалите это, и вы должны быть в порядке.