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

我希望这段代码足以让我获得响应的隐式缓存。我不确定它是否有效,因为我在发送请求时假设:

  1. 它会首先命中缓存并将其发送到 onresponse

  2. 然后当结果来自远程服务器时,它会将其提供给 onresponse

更新:

我想出了如何手动检索缓存并将其重建为 JSONObject 并通过 OnResponse 函数发送,但考虑到隐式缓存,这似乎效率不高。JsonObjectRequest 类应返回 JSONObject 作为缓存条目,而不是原始响应数据。

但我仍然有兴趣知道我是否犯了一些错误。

模棱两可完全是由于缺乏文档,所以如果我遗漏了一些非常明显的东西,我深表歉意。

4

4 回答 4

89

请参阅此答案 -使用 Google 的 Volley 设置缓存过期策略

这意味着 Volley 仅根据标头“Cache-Control”、“Expires”、“maxAge”来决定是否缓存响应。

您可以做的是更改此方法 com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response) 并忽略这些标头、集合entry.softTtlentry.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));
    }

}
于 2013-05-31T07:46:53.303 回答
5

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

于 2013-10-11T21:00:49.043 回答
2

也为 oleksandr_yefremov 和 skyfishjy +1,并在此处提供适用于 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 答案。在生成的 json 可以缓存 3 分钟(实时)和 24 小时(已过期但仍然可用)的任何地方使用 CachingStringRequest 类。样品请求:

CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback);

并在回调对象的 onResponse() 函数中,解析 json。设置你想要的任何缓存限制——你可以参数化为每个请求添加自定义过期时间。

为了好玩,请在一个简单的应用程序中尝试这个,该应用程序下载 json 并呈现下载的信息。在第一次成功下载填充缓存后,在缓存处于活动状态时观察快速渲染,因为您改变方向(如果实时缓存命中,则不会发生下载)。现在杀死应用程序,等待 3 分钟让缓存命中过期(但不是 24 小时才能从缓存中删除),启用飞行模式,然后重新启动应用程序。Volley 错误回调将发生,并且“成功”的 onResponse() 回调将从缓存的数据中发生,从而允许您的应用程序既可以呈现内容,也可以知道/警告它来自过期的缓存。

这种缓存的一种用途是避免加载器和其他处理方向变化的方法。如果请求通过 Volley 单例,并且结果被缓存,则通过方向更改发生的刷新将由 Volley 自动从缓存中快速呈现,无需加载器。

当然,这并不符合所有要求。YMMV

于 2014-09-18T19:53:48.877 回答
0

我能够通过扩展StringRequest和替换我想强制缓存的请求来强制 Volley 缓存所有响应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);
    }
}
于 2017-08-09T09:00:18.290 回答