Кэширование Android Volley + JSONObjectRequest
public class CustomRequest extends JsonObjectRequest {
public CustomRequest(String url, JSONObject params,
Listener<JSONObject> listener, ErrorListener errorListener)
throws JSONException {
super(Method.POST,url, params, listener,
errorListener);
this.setShouldCache(Boolean.TRUE);
}
}
Я надеялся, что этого фрагмента кода будет достаточно для того, чтобы я мог получить неявное кэширование ответов. Я не уверен, работает он или нет, потому что я был в предположении, когда отправлен запрос:
-
он сначала ударил бы по кешу и отправил его на ответ
-
тогда, когда результаты пройдут с удаленного сервера, он предоставит его onresponse
Update:
Я понял, как вручную извлечь кеш и восстановить его в JSONObject и отправить его через функцию OnResponse, но это не кажется эффективным, учитывая, что существует неявное кэширование. Класс JsonObjectRequest должен возвращать JSONObject в качестве кэшированной записи вместо необработанных данных ответа.
Но мне все еще интересно узнать, не ошибаюсь ли я.
Неоднозначность объясняется только отсутствием документации, поэтому я извиняюсь, если у меня что-то совершенно очевидно.
Ответы
Ответ 1
Посмотрите этот ответ - Установите политику истечения срока действия кэша с помощью Google Volley
Это означает, что Volley решает, следует ли кэшировать ответ или нет, основываясь только на заголовках "Cache-Control", а затем "Expires", "maxAge".
Что вы можете сделать, это изменить этот метод
com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response)
и игнорировать эти заголовки, задайте поля entry.softTtl
и entry.ttl
для любого значения, которое вам подходит, и используйте свой метод в своем классе запросов. Вот пример:
/**
* Extracts a {@link Cache.Entry} from a {@link NetworkResponse}.
* Cache-control headers are ignored. SoftTtl == 3 mins, ttl == 24 hours.
* @param response The network response to parse headers from
* @return a cache entry for the given response, or null if the response is not cacheable.
*/
public static Cache.Entry parseIgnoreCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
long serverDate = 0;
String serverEtag = null;
String headerValue;
headerValue = headers.get("Date");
if (headerValue != null) {
serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
}
serverEtag = headers.get("ETag");
final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background
final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
final long softExpire = now + cacheHitButRefreshed;
final long ttl = now + cacheExpired;
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = ttl;
entry.serverDate = serverDate;
entry.responseHeaders = headers;
return entry;
}
Используйте этот метод в своем классе Request следующим образом:
public class MyRequest extends com.android.volley.Request<MyResponse> {
...
@Override
protected Response<MyResponse> parseNetworkResponse(NetworkResponse response) {
String jsonString = new String(response.data);
MyResponse MyResponse = gson.fromJson(jsonString, MyResponse.class);
return Response.success(MyResponse, HttpHeaderParser.parseIgnoreCacheHeaders(response));
}
}
Ответ 2
oleksandr_yefremov предоставляет отличные коды, которые могут помочь вам при работе с кеш-стратегией Android Volley, особенно когда REST API имеет неправильные заголовки "Cache-Control" или вам просто нужно больше контролировать собственную стратегию кеша приложений.
Ключ HttpHeaderParser.parseCacheHeaders(NetworkResponse response))
. Если вы хотите иметь собственную стратегию кэширования. Замените его parseIgnoreCacheHeaders(NetworkResponse response)
в соответствующем классе.
Если ваш класс расширяет JsonObjectRequest, перейдите в JsonObjectRequest и найдите
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),HttpHeaderParser.parseCacheHeaders(response));
}catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
и замените HttpHeaderParser.parseCacheHeaders(response)
на HttpHeaderParser.parseIgnoreCacheHeaders
Ответ 3
+1 для oleksandr_yefremov и skyfishjy также и предлагает здесь конкретный класс многократного использования, подходящий для json или других API на основе строк:
public class CachingStringRequest extends StringRequest {
public CachingStringRequest(int method, String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(method, url, listener, errorListener);
}
public CachingStringRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(url, listener, errorListener);
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, parseIgnoreCacheHeaders(response));
}
}
где функция parseIgnoreCacheHeaders() исходит из ответа oleksandr_yefremov выше. Используйте класс CachingStringRequest везде, где результирующий json может кэшировать в течение 3 минут (вживую) и 24 часа (истек, но все еще доступен). Пример запроса:
CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback);
и внутри функции обратного вызова onResponse(), проанализируйте json. Задайте любые ограничения кэширования, которые вы хотите - вы можете параметризовать, чтобы добавить пользовательское истечение за запрос.
Для удовольствия попробуйте это в простом приложении, которое загружает json и отображает загруженную информацию. Заполнив кеш первой успешной загрузкой, следите за быстрым рендерингом, когда вы меняете ориентацию, в то время как кеш живет (загрузка не происходит с учетом хита в реальном времени). Теперь убейте приложение, подождите 3 минуты, пока истечет срок его действия (но не 24 часа для его удаления из кеша), включите режим полета и перезапустите приложение. Будет вызван обратный вызов ошибки Volley, и "успешный" ответ на запрос onResponse() будет происходить из кэшированных данных, что позволит вашему приложению как отображать контент, так и знать/предупреждать, что он пришел из истекшего кеша.
Одним из способов такого кэширования было бы устранить загрузчики и другие способы обращения с изменением ориентации. Если запрос проходит через Singleton Volley, и результаты кэшируются, обновления, которые происходят с помощью изменения ориентации, быстро отображаются из кеша автоматически с помощью Volley без Loader.
Конечно, это не соответствует всем требованиям. YMMV
Ответ 4
Я смог заставить Volley кэшировать все ответы, расширяя StringRequest
и заменяя запрос, который я хочу принудительно кэшировать с помощью CachingStringRequest
.
В переопределенном методе parseNetworkResponse
я удаляю заголовки Cache-Control
. Таким образом, Volley сохраняет ответ в встроенном кеше.
public class CachingStringRequest extends StringRequest {
private static final String CACHE_CONTROL = "Cache-Control";
public CachingStringRequest(int method,
String url,
Response.Listener<String> listener,
Response.ErrorListener errorListener) {
super(method, url, listener, errorListener);
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
// I do this to ignore "no-cache" headers
// and use built-in cache from Volley.
if (response.headers.containsKey(CACHE_CONTROL)) {
response.headers.remove(CACHE_CONTROL);
}
return super.parseNetworkResponse(response);
}
}