2013-05-27 9 views
48
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 implizite Zwischenspeicherung von Antworten zu erhalten. Ich bin mir nicht sicher, ob es funktioniert oder nicht, weil ich unter der Annahme war, wenn eine Anfrage gesendet wird:Android Volley + JSONObjectRequest Caching

  1. es den Cache zuerst treffen würde und dass

  2. dann, wenn die Ergebnisse onresponse senden kommen aus dem Remote-Server durch sie an die onresponse

-Update zur Verfügung stellen würde:

ich dachte, wie den Cache manuell abzurufen und rekonstruieren es i nto zu einem JSONObject und sende es über OnResponse-Funktion, aber das scheint nicht effizient zu sein, wenn man bedenkt, dass es implizites Caching gibt. Die JsonObjectRequest-Klasse sollte JSONObject als zwischengespeicherten Eintrag anstelle von unverarbeiteten Antwortdaten zurückgeben.

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

Die Ambiguität ist ausschließlich auf den Mangel an Dokumentation zurückzuführen, also entschuldige ich mich, wenn mir etwas ganz offensichtlich fehlt.

Antwort

86

diese Antwort finden - Set expiration policy for cache using Google's Volley

Das bedeutet, Volley, ob Antwort zwischenzuspeichern entscheidet oder nicht nur auf Header „Cache-Control“ basiert und dann „Läuft ab“, „maxAge“.

Was Sie tun können, ist diese Methode ändern com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response) und ignorieren diese Header, setzen entry.softTtl und entry.ttl Felder auf den Wert, der für Sie arbeitet und Ihre Methode in Ihrer Anfrage-Klasse verwenden. Hier 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 Anfrage 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)); 
    } 

} 
+1

bedeutet das die Cache über dauern wird, onDestroy? Damit das nächste Mal die App erstellt wird, wird es aus dem Cache abrufen? – gaara87

+3

Ja, der Cache wird nicht nur im Speicher, sondern auch auf der Festplatte gespeichert (Details siehe DiskBasedCache-Klasse). Als Schnelltest laden Sie einige Daten, beenden Sie Ihre App, schalten Sie WLAN oder 3g aus und geben Sie Ihre App erneut ein. Sie können die Cache-Größe auch im Feld "mMaxCacheSizeInBytes" angeben. –

+2

Ja, es wird gespeichert, während ich in der App bin, aber wenn ich die App verlasse und in die App zurückkehre, gibt das Abrufen aus dem Cache null zurück. Daher die Frage, ob es zwischen den Aktivitätslebenszyklen lebt. – gaara87

5

oleksandr_yefremov bietet große Codes, die Ihnen helfen, wenn Sie mit dem Cache-Strategie von Android Volley zu tun, vor allem, wenn die Die REST-API verfügt über falsche "Cache-Control" -Header oder Sie möchten nur mehr Kontrolle über Ihre eigene App-Cache-Strategie.

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 entsprechende Klasse.

Wenn Ihre Klasse JsonObjectRequest erstreckt, zu JsonObjectRequest gehen und

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

+0

Ist es irgendwie möglich, das mit 'NetworkImageView' zu tun? – fiddler

2

+1 für oleksandr_yefremov und skyfishjy auch, und bietet hier eine konkrete, wiederverwendbare Klasse geeignet für json finden oder andere string-basierte APIs:

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() com es von der oleksandr_yefremov Antwort oben.Verwenden Sie die CachingStringRequest-Klasse an einem beliebigen Ort, an dem der resultierende JSON für 3 Minuten (live) und 24 Stunden (abgelaufen, aber noch verfügbar) in den Cache gestellt werden kann. Eine Beispielanforderung:

CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback); 

und innerhalb der Funktion onResponse() des Callback-Objekts den JSON analysieren. Legen Sie die gewünschten Caching-Limits fest - Sie können parametrisieren, um benutzerdefinierte Ablaufdaten pro Anfrage hinzuzufügen.

Zum Spaß, 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, sehen Sie sich das schnelle Rendering an, wenn Sie die Ausrichtung ändern, während der Cache aktiv ist (kein Download bei einem Live-Cache-Treffer). Töte nun die App, warte 3 Minuten, bis der Cache-Treffer abläuft (aber nicht 24 Stunden, bevor er aus dem Cache entfernt wird), aktiviere den Flugzeugmodus und starte die App neu. Der Volley-Fehler-Callback wird ausgeführt UND der "erfolgreiche" onResponse() -Rückruf erfolgt aus zwischengespeicherten Daten, sodass Ihre App sowohl Inhalte rendern als auch wissen/warnen kann, dass sie aus abgelaufenem Cache stammen.

Eine Anwendung dieser Art von Caching wäre es, Loader und andere Mittel zum Umgang mit Orientierungsänderungen zu vermeiden. Wenn eine Anfrage einen Volley-Singleton durchläuft und Ergebnisse zwischengespeichert werden, werden Aktualisierungen, die über eine Orientierungsänderung erfolgen, schnell aus dem Cache gerendert, automatisch von Volley ohne den Loader.

Dies entspricht natürlich nicht allen Anforderungen. YMMV

0

Ich konnte Volley dazu zwingen, alle Antworten zwischenzuspeichern, indem ich StringRequest erweitere und die Anforderung ersetze, die zwangsweise mit CachingStringRequest zwischengespeichert werden soll.

In überschriebenen Methode parseNetworkResponse ich entfernen Cache-Control Header. Auf diese Weise behält Volley die Antwort im eingebauten 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); 
    } 
}