Android Volley + Jsonobjectrequest Caching

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

j'espérais que ce morceau de code me suffirait pour obtenir une mise en cache implicite des réponses. Je ne suis pas sûr si cela fonctionne ou pas, parce que j'étais sous l'hypothèse lorsqu'une demande est envoyée:

  1. il frapperait d'abord le cache et envoyer cela à onresponse

  2. puis, lorsque les résultats proviennent du serveur distant, il les fournit à l'onresponse

mise à Jour:

j'ai compris comment récupérer manuellement le cache et le reconstruire dans un JSONObject et l'envoyer par la fonction OnResponse mais cela ne semble pas efficace compte tenu qu'il y a une mise en cache implicite. La classe JsonObjectRequest devrait renvoyer JSONObject comme entrée en cache au lieu des données brutes de réponse.

mais je suis toujours intéressé de savoir si je fais une erreur.

l'ambiguïté c'est uniquement dû au manque de documentation, donc je m'excuse si je manque quelque chose de très évident.

48
demandé sur gaara87 2013-05-28 02:18:24

4 réponses

voir cette réponse - définir la Politique d'expiration pour le cache en utilisant la Volley de Google

cela signifie que Volley décide si la réponse cache ou non est basée uniquement sur les en-têtes" Cache-Control "et ensuite" Expires","maxAge".

Ce que vous pourriez faire est de changer cette méthode com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response) et ignorez ces en-têtes, mettez les champs entry.softTtl et entry.ttl à n'importe quelle valeur qui fonctionne pour vous et utilisez votre méthode dans votre classe de requête. Voici un exemple:

/**
 * 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;
}

utilisez cette méthode dans votre classe de requête comme ceci:

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
répondu Oleksandr Yefremov 2017-05-23 12:02:56

oleksandr_yefremov fournit de grands codes qui peuvent vous aider lorsque vous avez affaire à la stratégie de cache de Android Volley, en particulier lorsque l'API REST a des en-têtes "Cache-Control" inappropriés ou vous voulez juste plus de contrôle sur votre propre stratégie de cache app.

la clé est HttpHeaderParser.parseCacheHeaders(NetworkResponse response)) . Si vous voulez avoir votre propre stratégie de cache. Le remplacer par parseIgnoreCacheHeaders(NetworkResponse response) dans de la classe correspondante .

si votre classe s'étend JsonObjectRequest, allez à JsonObjectRequest et trouver

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

et remplacer HttpHeaderParser.parseCacheHeaders(response) par HttpHeaderParser.parseIgnoreCacheHeaders

5
répondu skyfishjy 2013-10-11 21:00:49

+1 pour oleksandr_yefremov et skyfishjy aussi, et offrant ici une classe de béton, réutilisable adapté pour JSON ou D'autres APIs à base de cordes:

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

où la fonction parseIgnoreCacheHeaders () vient de la réponse oleksandr_yefremov ci-dessus. Utilisez la classe CachingStringRequest partout où le JSON résultant est ok pour le cache pendant 3 minutes (live) et 24 heures (expiré mais toujours disponible). Une demande d'échantillon:

CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback);

et dans la fonction onResponse() de l'objet callback, analysez le json. Définissez les limites de mise en cache que vous voulez--vous pouvez paramétrer pour ajouter une expiration personnalisée par requête.

pour le plaisir, essayez ceci dans une application simple qui télécharge json et rend les informations téléchargées. Après avoir rempli le cache avec le premier téléchargement réussi, regardez le rendu rapide que vous changez les orientations tandis que le cache est en direct (Aucun téléchargement ne se produit étant donné un succès de cache en direct). Maintenant, désactivez l'application, attendez 3 minutes pour que cache d'accès à échéance (mais pas 24 heures pour qu'il soit retiré du cache), activez le mode avion, et redémarrez l'application. Le callback D'erreur de Volley se produira, et le callback "réussi" d'onResponse() se produira à partir de données cachées, permettant à votre application à la fois de rendre du contenu et aussi de savoir/prévenir qu'il provient d'un cache expiré.

L'une des utilisations de ce type de cache serait d'éviter les chargeurs et autres moyens de faire face au changement d'orientation. Si une demande passe par une Volley singleton, et les résultats sont mis en cache, les rafraîchissements qui se produisent via le changement d'orientation sont rendus rapidement depuis le cache, automatiquement par Volley, sans le chargeur.

bien sûr, cela ne correspond pas à toutes les exigences. YMMV

2
répondu larham1 2014-09-18 19:53:48

j'ai pu forcer Volley à mettre en cache toutes les réponses en étendant StringRequest et en remplaçant request I want to forcedly cache par CachingStringRequest .

Dans méthode substituée parseNetworkResponse je supprimer Cache-Control en-têtes. De cette façon, Volley maintient la réponse dans sa cache.

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
répondu sakydpozrux 2017-08-09 09:00:18