Android Volley + JSONObjectRequest-caching

Ik hoopte dat dit stukje code genoeg zou zijn voor het impliciet cachen van reacties. Ik weet niet zeker of het werkt of niet, omdat ik in de veronderstelling was toen een verzoek werd verzonden:

  1. het zou eerst de cache raken en dat naar onrespons sturen

  2. wanneer de resultaten van de externe server binnenkomen, geeft deze deze door aan de onresponse

Bijwerken:

Ik heb bedacht hoe ik de cache handmatig kan ophalen en reconstrueren in een JSONObject en deze via de functie OnResponse kan verzenden, maar dat lijkt niet efficiënt aangezien er impliciete caching is. De klasse JsonObjectRequest zou JSONObject moeten retourneren als het item in de cache in plaats van onbewerkte responsgegevens.

Maar ik ben nog steeds geïnteresseerd om te weten of ik een fout maak.

De dubbelzinnigheid is uitsluitend te wijten aan het gebrek aan documentatie, dus mijn excuses als ik iets heel duidelijks over het hoofd zie.


Antwoord 1, autoriteit 100%

Bekijk dit antwoord – Stel een vervalbeleid in voor cache met Google’s Volley

Dit betekent dat Volley beslist of de respons wel of niet in de cache wordt opgeslagen, alleen op basis van de headers “Cache-Control” en vervolgens “Expires”, “maxAge”.

Wat u zou kunnen doen, is deze methode wijzigen
com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response)
en negeer deze headers, stel de velden entry.softTtlen entry.ttlin op de waarde die voor u werkt en gebruik uw methode in uw verzoekklasse. Hier is een voorbeeld:

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

Gebruik deze methode in uw Request-klasse als volgt:

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

Antwoord 2, autoriteit 6%

oleksandr_yefremov biedt geweldige codes die u kunnen helpen bij het omgaan met de cachestrategie van Android Volley, vooral wanneer de REST API onjuiste “Cache-Control”-headers heeft of u gewoon meer controle wilt over uw eigen app-cachestrategie.

>

De sleutel is HttpHeaderParser.parseCacheHeaders(NetworkResponse response)). Als u uw eigen cachestrategie wilt hebben. Vervang het door parseIgnoreCacheHeaders(NetworkResponse response)in corresponderende klasse.

Als je klas JsonObjectRequest uitbreidt, ga dan naar JsonObjectRequest en zoek

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

en vervang HttpHeaderParser.parseCacheHeaders(response)door HttpHeaderParser.parseIgnoreCacheHeaders


Antwoord 3, autoriteit 2%

+1 ook voor oleksandr_yefremov en skyfishjy, en biedt hier een concrete, herbruikbare klasse aan die geschikt is voor json of andere op tekenreeksen gebaseerde API’s:

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

waarbij de functie parseIgnoreCacheHeaders() afkomstig is van het bovenstaande oleksandr_yefremov-antwoord. Gebruik de CachingStringRequest-klasse overal waar de resulterende json 3 minuten (live) en 24 uur (verlopen maar nog steeds beschikbaar) in de cache mag worden opgeslagen. Een voorbeeldverzoek:

CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback);

en binnen de functie onResponse() van het callback-object, parseert u de json. Stel de cachinglimieten in die je wilt – je kunt parameters instellen om een ​​aangepaste vervaldatum per verzoek toe te voegen.

Probeer dit voor de lol eens in een eenvoudige app die json downloadt en de gedownloade informatie weergeeft. Nadat je de cache met de eerste succesvolle download hebt gevuld, kun je de snelle weergave bekijken terwijl je van richting verandert terwijl de cache live is (er vindt geen download plaats bij een live-cachehit). Dood nu de app, wacht 3 minuten totdat die cache-hit is verlopen (maar niet 24 uur voordat deze uit de cache wordt verwijderd), schakel de vliegtuigmodus in en start de app opnieuw. De Volley-foutcallback zal plaatsvinden, EN de “succesvolle” onResponse()-callback zal plaatsvinden vanaf gegevens in de cache, waardoor uw app zowel inhoud kan weergeven als weet/waarschuwt dat deze afkomstig is uit een verlopen cache.

Een gebruik van dit soort caching zou zijn om Loaders en andere manieren om met oriëntatieverandering om te gaan, te voorkomen. Als een verzoek via een Volley-singleton gaat en de resultaten in de cache worden opgeslagen, worden verversingen die plaatsvinden via oriëntatieverandering snel uit de cache weergegeven, automatisch door Volley, zonder de Loader.

Natuurlijk voldoet dit niet aan alle vereisten. YMMV


Antwoord 4

Ik heb Volley kunnen forceren om alle reacties in de cache te plaatsen door StringRequestuit te breiden en het verzoek dat ik geforceerd wil cachen te vervangen door CachingStringRequest.

In de overschreven methode parseNetworkResponseverwijder ik Cache-Controlheaders. Op deze manier houdt Volley de reactie vast in de ingebouwde 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);
    }
}

Other episodes