【问题标题】:Retrofit offline request and response改造离线请求和响应
【发布时间】:2018-06-25 09:20:13
【问题描述】:

我已经阅读了很多关于我的问题的问题和答案,但我仍然无法理解如何解决它。

我需要从服务器获取响应并将其存储在缓存中。之后,当设备离线时,我想使用缓存响应。当设备在线时,我想准确地从服务器获取响应。

看起来没那么复杂。

这是我尝试这样做的方式(代码示例):

1)创建缓存的方法

Cache provideOkHttpCache() {
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        Cache cache = new Cache(this.getCacheDir(), cacheSize);
        return cache;
    }

2)创建一个拦截器来修改缓存控制头

 Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {

            CacheControl.Builder cacheBuilder = new CacheControl.Builder();
            cacheBuilder.maxAge(0, TimeUnit.SECONDS);
            cacheBuilder.maxStale(365, TimeUnit.DAYS);
            CacheControl cacheControl = cacheBuilder.build();

            Request request = chain.request();

            if (isOnline()) {
                request = request.newBuilder()
                        .cacheControl(cacheControl)
                        .build();
            }
            okhttp3.Response originalResponse = chain.proceed(request);
            if (isOnline()) {
                int maxAge = 20; // read from cache
                okhttp3.Response response = originalResponse.newBuilder()
                        .header("cache-control", "public, max-age=" + maxAge)
                        .build();
                return response;
            } else {
                int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
                okhttp3.Response response = originalResponse.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .build();
                return response;
            }
        }
    };

3) 创建 OkHttpClient 并进行改造

OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(httpLoggingInterceptor)
            .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
            .cache(provideOkHttpCache())
            .build();

    retrofit = new Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl("https://randomuser.me/")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();

4) 网络调用方法

private void networkCall() {
    Log.v(TAG, "networkCall() is called");
    Call<RandomUsers> randomUsersCall = getRandomUserService().getRandomUsers(10);
    randomUsersCall.enqueue(new Callback<RandomUsers>() {
        @Override
        public void onResponse(Call<RandomUsers> call, @NonNull Response<RandomUsers> response) {
            if (response.isSuccessful()) {
                try {
                    Log.v(TAG, "headers = " + response.headers());

                } catch (Exception e) {
                    e.printStackTrace();
                }

                mAdapter = new RandomUserAdapter();
                mAdapter.setItems(response.body().getResults());
                recyclerView.setAdapter(mAdapter);
            } else if (response.code() == 504) {
                Log.v(TAG, "response body = " + response.raw().cacheResponse());
            }
        }

        @Override
        public void onFailure(Call<RandomUsers> call, Throwable t) {
            Log.v("cache-control", "response failure");
        }
    });
}

那么,问题来了: 该应用程序永远不会进入 p.2 中的离线代码块。 如果 max-age 仍然是合法的 Retrofit 使用缓存的响应。 如果响应在一段时间前被缓存并且 max-age 不合法,则有两种情况:

1) 设备在线:Retrofit 向服务器发出新请求(一切正常)

2)设备离线:调用networkCall()内的回调方法onFailure(代码示例的第4页)

我的代码有什么问题?我真的不明白为什么应用程序不能使用缓存控制的离线情况。

抱歉,文字过多。 谢谢!

【问题讨论】:

    标签: android caching retrofit okhttp


    【解决方案1】:

    解决了。

    诀窍在于结合InterceptorNetworkInterceptor

    步骤:

    1) 为两个拦截器分开REWRITE_CACHE_CONTROL_INTERCEPTOR,一个用于在线工作,另一个用于离线工作:

    Interceptor OFFLINE_INTERCEPTOR = new Interceptor() {
                @Override
                public okhttp3.Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    if (!isOnline()) {
                        int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
                        request = request.newBuilder()
                                .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                                .build();
                    }
    
                    return chain.proceed(request);
                }
            };
    
    
    Interceptor ONLINE_INTERCEPTOR = new Interceptor() {
                @Override
                public okhttp3.Response intercept(Chain chain) throws IOException {
                    okhttp3.Response response = chain.proceed(chain.request());
                     int maxAge = 60; // read from cache
                    return response.newBuilder()
                             .header("Cache-Control", "public, max-age=" + maxAge)
                            .build();
                }
            };
    

    2)给okHttpClient添加拦截器

            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                //.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
                .addInterceptor(OFFLINE_INTERCEPTOR)
                .addNetworkInterceptor(ONLINE_INTERCEPTOR)
                .cache(provideOkHttpCache())
                .build();
    

    Link to original article

    【讨论】:

    • 您好,请问您的回答中的 isOnline 是什么?
    • @Ispam isOnline() 返回(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE).isConnectedOrConnecting();的结果
    • @AntonProkopov 你能看看这个吗。我使用了您的代码,但有问题。 stackoverflow.com/questions/52876279/…
    • @Mena 您好,您只使用addInterceptor(OFFLINE_INTERCEPTOR)。看起来您还需要使用addNetworkInterceptor(ONLINE_INTERCEPTOR)。对我来说,诀窍是同时使用这两个拦截器。希望对您有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-28
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    • 2019-02-28
    • 2013-12-05
    相关资源
    最近更新 更多