Android Volley+JSONObjectRequest Caching
response android-volley (4)
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);
}
}
Esperaba que este fragmento de código fuera suficiente para obtener el almacenamiento en caché implícito de las respuestas. No estoy seguro si funciona o no, porque estaba bajo la suposición cuando se envía una solicitud:
golpearía primero el caché y lo enviaría a la respuesta
luego, cuando los resultados provengan del servidor remoto, lo proporcionarían a la respuesta
Actualizar:
Pensé cómo recuperar manualmente el caché y reconstruirlo en un JSONObject y enviarlo a través de la función OnResponse, pero eso no parece eficiente teniendo en cuenta que hay caché implícito. La clase JsonObjectRequest debería devolver JSONObject como la entrada en caché en lugar de los datos de respuesta sin formato.
Pero todavía estoy interesado en saber si estoy cometiendo algún error.
La ambigüedad se debe únicamente a la falta de documentación, por lo que me disculpo si me falta algo bastante obvio.
+1 para oleksandr_yefremov y skyfishjy también, y ofreciendo aquí una clase concreta y reutilizable adecuada para json u otras API basadas en cadenas:
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));
}
}
donde la función parseIgnoreCacheHeaders () proviene de la respuesta oleksandr_yefremov anterior. Use la clase CachingStringRequest en cualquier lugar que el json resultante esté bien para cachear durante 3 minutos (en vivo) y 24 horas (caducado pero aún disponible). Una solicitud de muestra:
CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback);
y dentro de la función onResponse () del objeto de devolución de llamada, analizar el json. Establezca los límites de caché que desee: puede parametrizar para agregar caducidad personalizada por solicitud.
Para divertirse, intente esto en una aplicación simple que descarga json y representa la información descargada. Una vez que haya llenado la caché con la primera descarga exitosa, mire la representación rápida mientras cambia las orientaciones mientras la caché está activa (no se produce ninguna descarga dado un golpe de caché en vivo). Ahora elimine la aplicación, espere 3 minutos para que caduque el tiempo de caché (pero no 24 horas para que se elimine de la caché), habilite el modo avión y reinicie la aplicación. Se producirá la devolución de llamada por error de Volley, Y la devolución de llamada onResponse () "exitosa" se realizará a partir de datos almacenados en caché, lo que permitirá que su aplicación muestre contenido y también sepa / advierta que proviene de caché caducado.
Un uso de este tipo de almacenamiento en caché sería evitar los cargadores y otros medios para hacer frente al cambio de orientación. Si una solicitud pasa por un singleton de Volley, y los resultados se almacenan en caché, las actualizaciones que se producen a través del cambio de orientación se renderizan rápidamente desde la memoria caché, automáticamente por Volley, sin el Loader.
Por supuesto, esto no se ajusta a todos los requisitos. YMMV
Pude forzar a Volley a StringRequest
en caché todas las respuestas extendiendo StringRequest
y reemplazando la solicitud que quiero forzar en caché con CachingStringRequest
.
En el método reemplazado parseNetworkResponse
, parseNetworkResponse
encabezados de Cache-Control
. De esta forma, Volley persiste en la respuesta en su caché integrada.
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);
}
}
Ver esta respuesta: configure la política de caducidad para el caché con el voley de Google
Esto significa que Volley decide si almacenar en caché la respuesta o no solo en los encabezados "Cache-Control" y luego "Expira", "maxAge".
Lo que podría hacer es cambiar este método com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response)
e ignorar estos encabezados, establecer los campos entry.softTtl
y entry.ttl
a cualquier valor que funcione para usted y usar su método en su solicitud clase. Aquí hay un ejemplo:
/**
* 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;
}
Use este método en su clase de solicitud de esta manera:
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));
}
}
oleksandr_yefremov proporciona excelentes códigos que pueden ayudarte cuando tratas con la estrategia de caché de Android Volley, especialmente cuando la API REST tiene encabezados "Cache-Control" incorrectos o simplemente deseas más control sobre tu propia estrategia de caché de aplicaciones.
La clave es HttpHeaderParser.parseCacheHeaders(NetworkResponse response))
. Si quieres tener tu propia estrategia de caché. Reemplácelo con parseIgnoreCacheHeaders(NetworkResponse response)
en la clase correspondiente .
Si su clase extiende JsonObjectRequest, vaya a JsonObjectRequest y encuentre
@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));
}
}
y reemplazar HttpHeaderParser.parseCacheHeaders(response)
con HttpHeaderParser.parseIgnoreCacheHeaders