如何在 Android 上使用 HttpsURLConnection 和 HttpResponseCache 强制缓存?

Posted

技术标签:

【中文标题】如何在 Android 上使用 HttpsURLConnection 和 HttpResponseCache 强制缓存?【英文标题】:How to force caching with HttpsURLConnection and HttpResponseCache on Android? 【发布时间】:2018-06-03 13:47:27 【问题描述】:

我有以下方法从 wiktionary.org 请求页面,问题是服务器在标头中返回 Cache-control => private, must-revalidate, max-age=0,这阻止了 HttpsURLConnection 存储请求。

有没有办法强制缓存这些页面?

protected static synchronized String getUrlContent(String url) throws ApiException 
    if (sUserAgent == null) 
        throw new ApiException("User-Agent string must be prepared");
    

    try 
        URL obj = new URL(url);
        HttpsURLConnection connection = (HttpsURLConnection) obj.openConnection();

        connection.setRequestMethod("GET");
        connection.setRequestProperty("User-Agent", sUserAgent);
        //connection.addRequestProperty("Cache-Control", "max-stale");
        //connection.addRequestProperty("Cache-Control", "public, max-age=3600");
        //connection.addRequestProperty("Cache-Control", "only-if-cached");

        int responseCode = connection.getResponseCode();
        if (responseCode != HttpURLConnection.HTTP_OK)  // success
            throw new ApiException("Invalid response from server: " + responseCode);
        

        InputStream inputStream = connection.getInputStream();
        ByteArrayOutputStream content = new ByteArrayOutputStream();

        // Read response into a buffered stream
        int readBytes = 0;
        while ((readBytes = inputStream.read(sBuffer)) != -1) 
            content.write(sBuffer, 0, readBytes);
        

        HttpResponseCache cache = HttpResponseCache.getInstalled();
        if (cache != null) 
            Log.w("!!!", "Cache hit count: " + cache.getHitCount());
            //connection.addRequestProperty("Cache-Control", "public, max-age=3600");
            Log.w("!!!", "Cache-Control: " + connection.getHeaderField("Cache-Control"));
            //cache.put(new URI(url), connection);
        

        // Return result from buffered stream
        return new String(content.toByteArray());

     catch (Exception e) 
        throw new ApiException("Problem communicating with API", e);
    

更新:

仍然无法通过okhttp interceptors 获得缓存命中

static private OkHttpClient client;
static private Cache cache;

public static OkHttpClient getClient() 
    if (client == null) 
        File cacheDirectory = new File(App.getInstance().getCacheDir().getAbsolutePath(), "HttpCache");
        cache = new Cache(cacheDirectory, 1024 * 1024);
        client = new OkHttpClient.Builder()
                .cache(cache)
                .addInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR).build();
    
    return client;


/** Dangerous interceptor that rewrites the server's cache-control header. */
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() 
    @Override public Response intercept(Interceptor.Chain chain) throws IOException 
        Response originalResponse = chain.proceed(chain.request());
        return originalResponse.newBuilder()
                .header("Cache-Control", "max-age=60")
                .build();
    
;

protected static synchronized String getUrlContent(String url) throws ApiException 
    try 

        OkHttpClient httpClient = getClient();

        Request request = new Request.Builder()
                .url(url)
                .build();

        Response response = httpClient.newCall(request).execute();

        Log.w("!!!", "hitCount: " + cache.hitCount());

        return response.body().string();

     catch (Exception e) 
        throw new ApiException("Problem communicating with API", e);
    

【问题讨论】:

为什么不自己缓存它们,在 HTTP 请求层之上(例如,在您的存储库中)? @CommonsWare 如果我将域更改为支持缓存的其他站点,那么我需要检查缓存命中,以免自己缓存数据。似乎HttpsURLConnectionHttpResponseCache 都没有办法检查。另外,我在想也许我可以更改连接对象中的标头并将其存储在缓存中,就像cache.put(uri, connection); 一样,但似乎不可能。 “如果我将域更改为支持缓存的其他站点,那么我需要检查缓存命中,以免自己缓存数据”——或者,告诉 HTTP 堆栈不缓存,在应用层处理。我不知道HttpsURLConnection 是否提供,尽管 OkHttp 提供。可能有一个 OkHttp 配置可以处理您的目标,尽管我在快速扫描中没有看到。 @CommonsWare 用 okhttp 更新了我的问题,但仍然无法获得缓存命中。 【参考方案1】:

初始化 OKHttpClient 时请使用addNetworkInterceptor 而不是addInterceptor 重写缓存控制。

【讨论】:

我可以将User-Agent 放在同一个拦截器中,还是需要第二个,然后用addInterceptor 代替? 为什么需要user-agent 的拦截器?只需在构建请求时添加即可。 我明白了,你的意思是Request.Builder().addHeader()...这样就可以了,谢谢。

以上是关于如何在 Android 上使用 HttpsURLConnection 和 HttpResponseCache 强制缓存?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 上使用 javax.smartcardio 包?

如何在 Android 相机上使用意图添加功能裁剪图像?

如何在 Android 上使用 QOpenGLWidget?

如何在Android上打破通知的内容

如何在 Android 上使用嵌入式 Flash?

如何在 java (Android) 上使用字符串