简体   繁体   English

Android Volley + JSONObjectRequest缓存

[英]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);
    }
}

I was hoping that this piece of code would be enough for me to get implicit caching of responses. 我希望这段代码足以让我得到隐式缓存的响应。 I'm not sure if it works or not, because i was under the assumption when a request is sent: 我不确定它是否有效,因为我在发送请求时假设:

  1. it would hit the cache first and send that to onresponse 它会首先点击缓存并将其发送到响应

  2. then when the results come through from the remote server it would provide it to the onresponse 然后,当结果从远程服务器传来时,它会将其提供给响应

Update: 更新:

I figured how to manually retrieve the cache and reconstruct it into a JSONObject and send it through OnResponse function but that doesn't seem to efficient considering there is implicit caching. 我想到了如何手动检索缓存并将其重建为JSONObject并通过OnResponse函数发送它,但考虑到隐式缓存,这似乎并不高效。 JsonObjectRequest class should return JSONObject as the cached entry instead of raw response data. JsonObjectRequest类应该返回JSONObject作为缓存条目而不是原始响应数据。

But i'm still interested to know if i'm making some mistake. 但我仍然有兴趣知道我是否犯了一些错误。

The ambiguity is solely due to the lack of documentation, so i apologize if i'm missing something quite obvious. 模糊性完全是由于缺乏文档,所以如果我遗漏了一些非常明显的东西,我会道歉。

See this answer - Set expiration policy for cache using Google's Volley 请参阅此答案 - 使用Google的Volley设置缓存的过期策略

This means Volley decides whether to cache response or not based only on headers "Cache-Control" and then "Expires", "maxAge". 这意味着Volley仅根据标题“Cache-Control”然后“Expires”,“maxAge”决定是否缓存响应。

What you could do is change this method com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response) and ignore these headers, set entry.softTtl and entry.ttl fields to whatever value works for you and use your method in your request class. 您可以做的是更改此方法com.android.volley.toolbox.HttpHeaderParser.parseCacheHeaders(NetworkResponse response)并忽略这些标头,将entry.softTtlentry.ttl字段设置为适合您的任何值,并在您的请求中使用您的方法类。 Here is an example: 这是一个例子:

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

Use this method in your Request class like this: 在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));
    }

}

oleksandr_yefremov provides great codes that can help you when you dealing with cache strategy of Android Volley, especially when the REST API has improper "Cache-Control" headers or you just want more control on your own app cache strategy. oleksandr_yefremov提供了很好的代码,可以帮助您处理Android Volley的缓存策略,特别是当REST API具有不正确的“缓存控制”标头或您只是想要更多控制您自己的应用缓存策略时。

The key is HttpHeaderParser.parseCacheHeaders(NetworkResponse response)) . 关键是HttpHeaderParser.parseCacheHeaders(NetworkResponse response)) If you want to have your own cache strategy. 如果您想拥有自己的缓存策略。 Replace it with parseIgnoreCacheHeaders(NetworkResponse response) in corresponding class . 将其替换为相应类中的 parseIgnoreCacheHeaders(NetworkResponse response)

If your class extends JsonObjectRequest, go to JsonObjectRequest and find 如果您的类扩展了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));
        }
}

and replace HttpHeaderParser.parseCacheHeaders(response) with HttpHeaderParser.parseIgnoreCacheHeaders 和替换HttpHeaderParser.parseCacheHeaders(response)HttpHeaderParser.parseIgnoreCacheHeaders

+1 for oleksandr_yefremov and skyfishjy also, and offering here a concrete, reusable class suitable for json or other string-based APIs: 为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));
    }
}

where the function parseIgnoreCacheHeaders() comes from the oleksandr_yefremov answer above. 函数parseIgnoreCacheHeaders()来自上面的oleksandr_yefremov答案。 Use the CachingStringRequest class anywhere that the resulting json is ok to cache for 3 minutes (live) and 24 hours (expired but still available). 在生成的json可以缓存3分钟(实时)和24小时(已过期但仍可用)的任何地方使用CachingStringRequest类。 A sample request: 样品申请:

CachingStringRequest stringRequest = new CachingStringRequest(MY_API_URL, callback);

and within the callback object's onResponse() function, parse the json. 并在回调对象的onResponse()函数中,解析json。 Set whatever caching limits you want--you could parameterize to add custom expiration per request. 设置您想要的任何缓存限制 - 您可以参数化以按请求添加自定义过期。

For fun, try this in a simple app that downloads json and renders the downloaded info. 为了好玩,请在下载json并呈现下载信息的简单应用程序中尝试此操作。 Having filled the cache with the first successful download, watch the speedy rendering as you change orientations while cache is live (no download occurs given a live cache hit). 在第一次成功下载后填充缓存,在缓存处于活动状态时更改方向时观察快速渲染(在实时缓存命中时不会发生下载)。 Now kill the app, wait 3 minutes for that cache hit to expire (but not 24 hours for it to be removed from cache), enable airplane mode, and restart the app. 现在杀死应用程序,等待3分钟以使缓存命中过期(但不是24小时将其从缓存中删除),启用飞行模式,然后重新启动应用程序。 The Volley error callback will occur, AND the "successful" onResponse() callback will occur from cached data, allowing your app to both render content and also know/warn that it came from expired cache. 将发生Volley错误回调,并且“成功”onResponse()回调将从缓存数据发生,允许您的应用程序既渲染内容又知道/警告它来自过期缓存。

One use of this kind of caching would be to obviate Loaders and other means of dealing with orientation change. 这种缓存的一种用途是避免加载器和其他处理方向变化的方法。 If a request goes through a Volley singleton, and results are cached, refreshes that happen via orientation change are rendered quickly from cache, automatically by Volley, without the Loader. 如果请求通过Volley单例,并且结果被缓存,则通过方向更改发生的刷新将从缓存中快速呈现,由Volley自动呈现,而不需要Loader。

Of course, this doesn't fit all requirements. 当然,这并不符合所有要求。 YMMV 因人而异

I was able to force Volley to cache all responses by extending StringRequest and replacing request I want to forcibly cache with CachingStringRequest . 我能够通过扩展StringRequest强制Volley缓存所有响应,并替换我想用CachingStringRequest强制缓存的CachingStringRequest

In overridden method parseNetworkResponse I remove Cache-Control headers. 在重写方法parseNetworkResponse我删除了Cache-Control标头。 This way Volley is persisting the response in it's built-in cache. 这样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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM