android - OkHttp3 缓存似乎未通过 Retrofit 2 检查

标签 android caching retrofit2 okhttp

我正在尝试使用 Retrofit (2.1.0) 和 OkHttp (3.3.1) 设置 HTTP 缓存。我看过很多与此主题相关的帖子,但都没有帮助。

我写了一些单元测试来查看缓存是如何工作的。它工作得很好,但一旦集成到我的应用程序中,魔法就结束了。我将首先向您展示我的实现,然后解释我的一些调查。

首先,这是我的 Retrofit 实例:

    OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);

    OkHttpClient client = httpBuilder
            .addNetworkInterceptor(INTERCEPTOR_RESPONSE_SET_CACHE)
            .addNetworkInterceptor(INTERCEPTOR_REQUEST_ADD_CHECKSUM)
            .addInterceptor(loggingInterceptor)
            .cache(cacheHttpClient).build();

    Retrofit retrofit = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .baseUrl(BASE_URL)
            .build();

这里是拦截器添加一个头来设置缓存控制:

    private final Interceptor INTERCEPTOR_RESPONSE_SET_CACHE = new Interceptor() {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Response response = chain.proceed(chain.request());
            response = response.newBuilder()
                    .header("Cache-Control", "max-age=600") //+ Integer.toString(3600 * 5)
                    .build();
            return response;
        }
    };

最后一个拦截器添加了 2 个 URL 参数:

 private static final Interceptor INTERCEPTOR_REQUEST_ADD_CHECKSUM = new Interceptor() {
        @Override
        public Response intercept(Interceptor.Chain chain) throws IOException {
            HttpUrl url = chain.request().url();
            url = url.newBuilder().addQueryParameter("rd", "random1").addQueryParameter("chk","check1").build();
            Request request = chain.request().newBuilder().url(url).build();
            return chain.proceed(request);
        }
    };

最后是我服务的单一方法:

 @Headers("Cache-Control: public, max-stale=500")
 @GET("/get_data")
 Call<DataResponse> getData(@Query("year") int year, @Query("month") int month, @Query("day") int day);

关于我的调查,我设置了一个拦截器记录器(应用端,而不是网络)来查看发生了什么。我可以在日志中看到诸如“Cache-Control: public, max-stale=500”之类的行。这意味着(至少对我而言) header 应该为 OkHttp 客户端提供检查缓存的机会。

缓存本身似乎已正确初始化。当我创建它时,我强制初始化并记录缓存中存在的所有 url。下面是它的实现方式:

File httpCacheDirectory = new File(getCacheDir(), "responses");
        httpCacheDirectory.getParentFile().mkdirs();
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        Cache cache = new Cache(httpCacheDirectory, cacheSize);
        try {
            cache.initialize();
            Iterator<String> iterator = cache.urls();
            Log.i(TAG, "URLs in cacheHttpClient : ");
            while (iterator.hasNext()) {
                Log.i(TAG, iterator.next());
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.i(TAG, "CACHE NOT INIT");
        }

当我在 Wifi 可用的情况下启动我的应用程序时,我得到了预期的响应。然后我终止我的应用程序,禁用 Wifi 并重新启动该应用程序。我希望缓存此时可以提供数据。但它失败了,我只能在日志中看到 OkHttp 打印的行:

HTTP FAILED: java.net.UnknownHostException: Unable to resolve host "my-domain.com": No address associated with hostname

最后一件事,在 RFC 2616 中,可以阅读:

max-stale : Indicates that the client is willing to accept a response that has exceeded its expiration time. If max-stale is assigned a value, then the client is willing to accept a response that has exceeded its expiration time by no more than the specified number of seconds. If no value is assigned to max-stale, then the client is willing to accept a stale response of any age.

当我没有指定一个值时,它实际上可以工作(即使 Wifi 关闭我也会收到响应)。现在这是我发现让它“工作”的唯一方法。所以也许我只是误解了缓存控制指令!?

此时我真的很困惑。我真的很想能够使用 OkHttp 缓存系统,但不知何故我遗漏了一些东西。

感谢您阅读所有文字!

最佳答案

使用该方法创建缓存的okkhttpclient

private OkHttpClient createCachedClient(final Context context) {
        File httpCacheDirectory = new File(context.getCacheDir(), "cache_file");

        Cache cache = new Cache(httpCacheDirectory, 20 * 1024 * 1024);
        OkHttpClient okHttpClient = new OkHttpClient();
        okHttpClient.setCache(cache);
        okHttpClient.interceptors().add(
                new Interceptor() {
                    @Override
                    public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
                        Request originalRequest = chain.request();
                        String cacheHeaderValue = isOnline(context)
                                ? "public, max-age=2419200"
                                : "public, only-if-cached, max-stale=2419200" ;
                        Request request = originalRequest.newBuilder().build();
                        com.squareup.okhttp.Response response = chain.proceed(request);
                        return response.newBuilder()
                                .removeHeader("Pragma")
                                .removeHeader("Cache-Control")
                                .header("Cache-Control", cacheHeaderValue)
                                .build();
                    }
                }
        );
        okHttpClient.networkInterceptors().add(
                new Interceptor() {
                    @Override
                    public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
                        Request originalRequest = chain.request();
                        String cacheHeaderValue = isOnline(context)
                                ? "public, max-age=2419200"
                                : "public, only-if-cached, max-stale=2419200" ;
                        Request request = originalRequest.newBuilder().build();
                        com.squareup.okhttp.Response response = chain.proceed(request);
                        return response.newBuilder()
                                .removeHeader("Pragma")
                                .removeHeader("Cache-Control")
                                .header("Cache-Control", cacheHeaderValue)
                                .build();
                    }
                }
        );
        return okHttpClient;
    }

    private boolean isOnline(Context context) {
        ConnectivityManager connectivity = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivity != null) {
            NetworkInfo[] info = connectivity.getAllNetworkInfo();
            if (info != null)
                for (int i = 0; i < info.length; i++)
                    if (info[i].getState() == NetworkInfo.State.CONNECTED) {
                        return true;
                    }
        }
        return false;


        }

调用createCachedClient()方法创建OkHttpClient 添加此客户端进行改造

OkHttpClient okHttpClient = createCachedClient(MainActivity.this);
Retrofit retrofit=new Retrofit.Builder()
         .client(okHttpClient)
         .baseUrl(API)
         .addConverterFactory(GsonConverterFactory
         .create()).build();

将此权限添加到 list

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

如果互联网第一次可用,它将调用该服务并缓存请求,下一次最多 2419200 毫秒,它将使用缓存提供响应。即使设备离线,它也不会在 2419200 毫秒内访问服务器。

关于android - OkHttp3 缓存似乎未通过 Retrofit 2 检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40532912/

相关文章:

android - 如何为 OkHttp Android 设置陈旧时间?

java - Infinispan-10.0.1.Final : No marshaller registered for Java type java. util.UUID

Wordpress 和 W3 Total Cache minifier

android - 如何引用API的shell命令向API发起POST请求?

java - Android - Retrofit2 - java.security.cert.CertPathValidatorException : Trust anchor for certification path not found

android - 如何使用 RxJava 处理回收站 View 的项目点击

android - isBluetoothA2dpOn 无法正常工作

android - 从短信/邮件链接到 Android 应用程序

javascript - Chrome 何时清除磁盘缓存?

java - 使用单键改造发送多个图像