webentwicklung-frage-antwort-db.com.de

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

Ich hatte gehofft, dass dieses Stück Code für mich ausreichen würde, um das implizite Zwischenspeichern von Antworten zu erhalten. Ich bin mir nicht sicher, ob es funktioniert oder nicht, weil ich davon ausging, als eine Anfrage gesendet wurde:

  1. es würde zuerst den Cache treffen und an eine Antwort senden

  2. wenn dann die Ergebnisse vom Remote-Server durchgekommen werden, wird sie der Antwortantwort zur Verfügung gestellt 

Aktualisieren:

Ich habe mir überlegt, wie man den Cache manuell abruft und in ein JSONObject rekonstruiert und über die OnResponse-Funktion sendet, aber das scheint nicht effizient zu sein, wenn man bedenkt, dass implizite Zwischenspeicherung vorliegt. Die JsonObjectRequest-Klasse sollte JSONObject als zwischengespeicherten Eintrag anstelle von rohen Antwortdaten zurückgeben.

Aber ich bin immer noch daran interessiert zu wissen, ob ich einen Fehler mache.

Die Mehrdeutigkeit ist einzig und allein auf mangelnde Dokumentation zurückzuführen. Ich entschuldige mich, wenn ich etwas offensichtliches vermisse.

48
gaara87

Diese Antwort anzeigen - Legen Sie die Ablaufrichtlinie für den Cache mithilfe von Volley von Google fest.

Dies bedeutet, dass Volley aufgrund der Header "Cache-Control" und dann "Expires", "maxAge" entscheidet, ob die Antwort zwischengespeichert werden soll oder nicht.

Was Sie tun können, ist diese Methode zu ändern com.Android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response) Und ignorieren Sie diese Header, setzen Sie entry.softTtl- und entry.ttl-Felder auf den für Sie geeigneten Wert und verwenden Sie Ihre Methode in Ihrer Anforderungsklasse. Hier ist ein Beispiel:

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

Verwenden Sie diese Methode in Ihrer Request-Klasse wie folgt:

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

oleksandr_yefremov bietet großartige Codes, die Ihnen beim Umgang mit der Cache-Strategie von Android Volley helfen können, insbesondere wenn die API REST unpassende "Cache-Control" -Header enthält oder Sie einfach mehr Kontrolle über Ihre eigene App-Cache-Strategie haben möchten.

Der Schlüssel ist HttpHeaderParser.parseCacheHeaders(NetworkResponse response)). Wenn Sie eine eigene Cache-Strategie haben möchten. Ersetzen Sie es durch parseIgnoreCacheHeaders(NetworkResponse response) in der entsprechenden Klasse

Wenn Ihre Klasse JsonObjectRequest erweitert, wechseln Sie zu JsonObjectRequest und suchen Sie 

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

und ersetze HttpHeaderParser.parseCacheHeaders(response) durch HttpHeaderParser.parseIgnoreCacheHeaders

5
skyfishjy

+1 für oleksandr_yefremov und skyfishjy, und hier eine konkrete, wiederverwendbare Klasse, die für Json- oder andere stringbasierte APIs geeignet ist:

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

wo die Funktion parseIgnoreCacheHeaders () von der Antwort oleksandr_yefremov oben kommt. Verwenden Sie die CachingStringRequest-Klasse an einem Ort, an dem der resultierende Json für 3 Minuten (live) und 24 Stunden (abgelaufen, aber noch verfügbar) in Ordnung ist. Eine Musteranfrage:

CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback);

und analysieren Sie in der Funktion onResponse () des Callback-Objekts den Json. Legen Sie die gewünschten Zwischenspeicherungslimits fest. Sie können parametrisieren, um den benutzerdefinierten Ablauf pro Anforderung hinzuzufügen.

Versuchen Sie dies in einer einfachen App, die Json herunterlädt und die heruntergeladenen Informationen rendert. Wenn Sie den Cache mit dem ersten erfolgreichen Download gefüllt haben, beobachten Sie das schnelle Rendern, während Sie die Ausrichtung ändern, während der Cache live ist (bei einem Live-Cache-Treffer wird kein Download durchgeführt). Beenden Sie nun die App, warten Sie 3 Minuten, bis der Cache-Treffer abläuft (nicht jedoch 24 Stunden, um ihn aus dem Cache zu entfernen), aktivieren Sie den Flugmodus und starten Sie die App erneut. Der Callback für Volley-Fehler tritt auf UND der "erfolgreiche" onResponse () - Callback wird aus zwischengespeicherten Daten ausgelöst, sodass Ihre App sowohl Inhalte rendern als auch wissen kann, dass sie aus einem abgelaufenen Cache stammen. 

Eine Verwendung dieser Art von Zwischenspeicherung würde darin bestehen, Loader und andere Mittel zur Bewältigung von Orientierungsänderungen zu vermeiden. Wenn eine Anfrage ein Volley-Singleton durchläuft und die Ergebnisse zwischengespeichert werden, werden Aktualisierungen, die durch Ausrichtungsänderung erfolgen, schnell und automatisch vom Volley-Cache ohne den Loader ausgeführt. 

Das entspricht natürlich nicht allen Anforderungen. YMMV

2
larham1

Ich konnte Volley zwingen, alle Antworten zwischenzuspeichern, indem ich StringRequest erweiterte und die Anforderung, die ich zwangsweise zwischenspeichern möchte, durch CachingStringRequest ersetzt.

In der überschriebenen Methode parseNetworkResponse entferne ich Cache-Control-Header. Auf diese Weise behält Volley die Antwort im eingebauten Cache bei.

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
sakydpozrux