Almacenamiento en caché de 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);
    }
}

Esperaba que este fragmento de código fuera suficiente para obtener el almacenamiento en caché implícito de las respuestas. No estoy seguro de si funciona o no, porque estaba bajo la suposición cuando se envía una solicitud:

  1. Primero golpearía la caché y lo enviaría a onresponse

  2. Luego, cuando los resultados vienen a través del servidor remoto que proporcionaría a la onresponse

Actualización:

Me imaginé cómo recuperar manualmente la caché y reconstruct it into a JSONObject and send it through onResponse function but that doesn't seem to efficient considering there is implicit caching. La clase JsonObjectRequest debe devolver JSONObject como la entrada en caché en lugar de los datos de respuesta sin procesar.

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, así que me disculpo si me estoy perdiendo algo bastante obvio.

Author: gaara87, 2013-05-28

4 answers

Ver esta respuesta - Establecer la política de caducidad para la caché utilizando Volley de Google

Esto significa que Volley decide si almacenar en caché la respuesta o no basándose solo en los encabezados "Cache-Control" y luego "Expires", "maxAge".

Lo que podría hacer es cambiar este método com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response) e ignora estas cabeceras, establece los campos entry.softTtl y entry.ttl a cualquier valor que funcione para ti y usa tu método en tu clase request. He aquí 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 como esto:

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));
    }

}
 87
Author: Oleksandr Yefremov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:56

Oleksandr_yefremov proporciona excelentes códigos que pueden ayudarlo cuando se trata de la estrategia de caché de Android Volley, especialmente cuando la API REST tiene encabezados incorrectos de "Control de caché" o simplemente desea más control sobre su propia estrategia de caché de aplicaciones.

La clave es HttpHeaderParser.parseCacheHeaders(NetworkResponse response)). Si quieres tener tu propia estrategia de caché. Sustitúyalo por parseIgnoreCacheHeaders(NetworkResponse response) en la clase correspondiente.

Si su clase extiende JsonObjectRequest, vaya a JsonObjectRequest y busque

@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 sustituir HttpHeaderParser.parseCacheHeaders(response) con HttpHeaderParser.parseIgnoreCacheHeaders

 5
Author: skyfishjy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 21:00:49

+ 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. Utilice la clase CachingStringRequest en cualquier lugar donde el json resultante esté bien para almacenar en caché 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 callback, analiza el json. Establezca los límites de almacenamiento en caché que desee could podría parametrizar para agregar caducidad personalizada por solicitud.

Por diversión, prueba esto en una aplicación simple que descarga json y renderiza la información descargada. Después de haber llenado la caché con la primera descarga exitosa, observe la rápida representación a medida que cambia de orientación mientras la caché está en vivo (no se produce ninguna descarga dada una coincidencia de caché en vivo). Ahora mata la aplicación, espera 3 minutos para que caduque el golpe de caché (pero no 24 horas para que se elimine de caché), habilite el modo avión y reinicie la aplicación. Se producirá la devolución de llamada de error Volley, Y la devolución de llamada" exitosa " onResponse() se producirá desde los datos almacenados en caché, lo que permitirá a tu aplicación tanto renderizar contenido como saber/advertir que proviene de la caché caducada.

Un uso de este tipo de almacenamiento en caché sería obviar los cargadores y otros medios de lidiar con el cambio de orientación. Si una solicitud pasa por un Volley singleton, y los resultados se almacenan en caché, las actualizaciones que ocurren a través del cambio de orientación son renderizado rápidamente desde caché, automáticamente por Volea, sin el Cargador.

Por supuesto, esto no se ajusta a todos los requisitos. YMMV

 2
Author: larham1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-18 19:53:48

Pude forzar a Volley a almacenar en caché todas las respuestas extendiendo StringRequest y reemplazando request I want to forcibly cache con CachingStringRequest.

En el método sobrescrito parseNetworkResponse elimino los encabezados Cache-Control. De esta manera Volley está persistiendo la respuesta en su caché incorporada.

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);
    }
}
 0
Author: sakydpozrux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-09 09:00:18