Кэширование Android Volley + JSONObjectRequest - программирование
Подтвердить что ты не робот

Кэширование 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);
    }
}

Я надеялся, что этого фрагмента кода будет достаточно для того, чтобы я мог получить неявное кэширование ответов. Я не уверен, работает он или нет, потому что я был в предположении, когда отправлен запрос:

  • он сначала ударил бы по кешу и отправил его на ответ

  • тогда, когда результаты пройдут с удаленного сервера, он предоставит его onresponse

Update:

Я понял, как вручную извлечь кеш и восстановить его в JSONObject и отправить его через функцию OnResponse, но это не кажется эффективным, учитывая, что существует неявное кэширование. Класс JsonObjectRequest должен возвращать JSONObject в качестве кэшированной записи вместо необработанных данных ответа.

Но мне все еще интересно узнать, не ошибаюсь ли я.

Неоднозначность объясняется только отсутствием документации, поэтому я извиняюсь, если у меня что-то совершенно очевидно.

4b9b3361

Ответ 1

Посмотрите этот ответ - Установите политику истечения срока действия кэша с помощью Google Volley

Это означает, что Volley решает, следует ли кэшировать ответ или нет, основываясь только на заголовках "Cache-Control", а затем "Expires", "maxAge".

Что вы можете сделать, это изменить этот метод com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response) и игнорировать эти заголовки, задайте поля entry.softTtl и entry.ttl для любого значения, которое вам подходит, и используйте свой метод в своем классе запросов. Вот пример:

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

Используйте этот метод в своем классе Request следующим образом:

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

}

Ответ 2

oleksandr_yefremov предоставляет отличные коды, которые могут помочь вам при работе с кеш-стратегией Android Volley, особенно когда REST API имеет неправильные заголовки "Cache-Control" или вам просто нужно больше контролировать собственную стратегию кеша приложений.

Ключ HttpHeaderParser.parseCacheHeaders(NetworkResponse response)). Если вы хотите иметь собственную стратегию кэширования. Замените его parseIgnoreCacheHeaders(NetworkResponse response) в соответствующем классе.

Если ваш класс расширяет JsonObjectRequest, перейдите в JsonObjectRequest и найдите

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

и замените HttpHeaderParser.parseCacheHeaders(response) на HttpHeaderParser.parseIgnoreCacheHeaders

Ответ 3

+1 для oleksandr_yefremov и skyfishjy также и предлагает здесь конкретный класс многократного использования, подходящий для json или других API на основе строк:

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

где функция parseIgnoreCacheHeaders() исходит из ответа oleksandr_yefremov выше. Используйте класс CachingStringRequest везде, где результирующий json может кэшировать в течение 3 минут (вживую) и 24 часа (истек, но все еще доступен). Пример запроса:

CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback);

и внутри функции обратного вызова onResponse(), проанализируйте json. Задайте любые ограничения кэширования, которые вы хотите - вы можете параметризовать, чтобы добавить пользовательское истечение за запрос.

Для удовольствия попробуйте это в простом приложении, которое загружает json и отображает загруженную информацию. Заполнив кеш первой успешной загрузкой, следите за быстрым рендерингом, когда вы меняете ориентацию, в то время как кеш живет (загрузка не происходит с учетом хита в реальном времени). Теперь убейте приложение, подождите 3 минуты, пока истечет срок его действия (но не 24 часа для его удаления из кеша), включите режим полета и перезапустите приложение. Будет вызван обратный вызов ошибки Volley, и "успешный" ответ на запрос onResponse() будет происходить из кэшированных данных, что позволит вашему приложению как отображать контент, так и знать/предупреждать, что он пришел из истекшего кеша.

Одним из способов такого кэширования было бы устранить загрузчики и другие способы обращения с изменением ориентации. Если запрос проходит через Singleton Volley, и результаты кэшируются, обновления, которые происходят с помощью изменения ориентации, быстро отображаются из кеша автоматически с помощью Volley без Loader.

Конечно, это не соответствует всем требованиям. YMMV

Ответ 4

Я смог заставить Volley кэшировать все ответы, расширяя StringRequest и заменяя запрос, который я хочу принудительно кэшировать с помощью CachingStringRequest.

В переопределенном методе parseNetworkResponse я удаляю заголовки Cache-Control. Таким образом, Volley сохраняет ответ в встроенном кеше.

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