改装 2 和 OKHttp 3 缓存响应在我的情况下不起作用



根据这个问题的答案,离线时可以用OKHttp使用缓存数据进行改装,我能够想出这个问题,但代码似乎无法缓存。我可能做错了什么?

这是我的okhttp客户端

    long SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MB
    Cache cache = new Cache(getDirectory(), SIZE_OF_CACHE);
    if (cache == null) {
        Toast.makeText(AppController.getInstance().getApplicationContext(), "could n0t set cache", Toast.LENGTH_SHORT).show();
    }
    client = new OkHttpClient
            .Builder()
            .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
            .cache(cache)
            .build();

添加我的网络拦截器如下:

 private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Response originalResponse = chain.proceed(chain.request());
            if (isConnected()) {
                int maxAge = 60; // read from cache for 1 minute
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, max-age=" + maxAge)
                        .build();
            } else {
                int maxStale = 60 * 60 * 24; // tolerate 1-day stale
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .build();
            }
        }
    };

我添加了这样的改装:

public static Retrofit getClient() {
        createCacheForOkHTTP();
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .client(client)
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }

//添加到我的活动中:

 ApiInterface apiService =
                ApiClient.getClient().create(ApiInterface.class);
        Call<MovieResponse> call = apiService.getPopularMoviesDetails(ApiKey, page);
        call.enqueue(new Callback<MovieResponse>() {
            @Override
            public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
                progressBar.setVisibility(View.GONE);
                mErrorView.setVisibility(View.GONE);
                if (response.isSuccessful()) {
                    movies = response.body().getResults();
                    movieAdapter.setMovieList(movies);
                    mRecyclerView.setAdapter(movieAdapter);
                } else {
                    Toast.makeText(getActivity(), "header" + response.headers() + "code" + response.code() + "errorbody" + response.errorBody() + "errorbody" + response.message(), Toast.LENGTH_SHORT).show();
                }
            }
            @Override
            public void onFailure(Call<MovieResponse> call, Throwable t) {
                progressBar.setVisibility(View.GONE);
                // Log error here since request failed
                Log.e(TAG, t.toString());
                mErrorView.setVisibility(View.VISIBLE);
            }
        });

//接口

 @GET("movie/popular")
    Call<MovieResponse> getPopularMoviesDetails(@Query("api_key") String apiKey, @Query("page") int page);

您不应该为了方便缓存而重写服务器的响应。最好让服务器的管理员在提供响应时包含缓存头。这样缓存就适用于所有客户端,而不仅仅是OkHttp。

也就是说,你是一个成年人,你有权走技术捷径,避免与你的服务器团队交谈。

首先,您需要更改代码以同时使用网络拦截器和应用程序拦截器。为什么是两个?好吧,当你向缓存发出请求时,网络拦截器还没有运行。当您将响应写入缓存时,应用程序拦截器还没有运行。

当你喜欢缓存时,你的应用程序拦截器会重写你的请求头,以包括这一点,并允许缓存提供过时的响应:

Cache-Control: only-if-cached, max-stale=86400

您的网络拦截器将重写服务器的响应标头,以便将所有响应缓存24小时:

Cache-Control: max-age=86400

不确定您是否已经修复了这个问题。我找到了一个好的/短期的解决方案,希望它能有所帮助。

我创建了一个CacheControlInterceptor。这有24小时的缓存,如果您处于脱机状态,并且您的响应不到24小时,您将获得缓存响应。

    public class CacheControlInterceptor implements Interceptor {
    private static final String CACHE_CONTROL_HEADER = "Cache-Control";
    private static final String MAX_AGE_HEADER_VALUE = "public, max-age=";
    private static final String MAX_STALE_HEADER_VALUE = "public, only-if-cached, max-stale=";
    // Specifies the maximum amount of time a resource will be considered fresh.
    private static final int MAX_AGE = 60;
    // Indicates that the client is willing to accept a response that has exceeded its expiration time.
    private static final int MAX_STALE = 60 * 60 * 24; // 24 hours cache.
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (WatchItApplication.hasNetwork()) {
            request = request.newBuilder()
                    .header(CACHE_CONTROL_HEADER, MAX_AGE_HEADER_VALUE + MAX_AGE).build();
        } else {
            request = request.newBuilder()
                    .header(CACHE_CONTROL_HEADER, MAX_STALE_HEADER_VALUE + MAX_STALE).build();
        }
        return chain.proceed(request);
    }
}

在这个方法上,我创建了OKHttpCliente。正如您所注意到的,我正在添加CacheControlInterceptor和缓存。

 private OkHttpClient createDefaultOkHttpClient() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
        return new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .connectTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
                .readTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
                .writeTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
                .addNetworkInterceptor(new HeaderInterceptor())
                .cache(new Cache(WatchItApplication.getInstance().getCacheDir(), 10 * 1024 * 1024))
                .addInterceptor(new CacheControlInterceptor())
                .addInterceptor(interceptor)
                .build();
    }

最后改造

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

如果可行,请告诉我。

我解决了这个问题。我就是这么做的。

private static OkHttpClient getCacheClient(final Context context) {
    Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            okhttp3.Response originalResponse = chain.proceed(chain.request());
            if (isConnected()) {
                // Internet available; read from cache for 0 day
                // Why? Reduce server load, better UX
                int maxAge = 0;
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, max-age=" + maxAge)
                        .build();
            } else {
                // No internet; tolerate cached data for 1 week
                int maxStale = 60 * 60 * 24 * 7;
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .build();
            }
        }
    };
    File httpCacheDirectory = new File(context.getCacheDir(), "cachedir");
    int size = 5 * 1024 * 1024;
    Cache cache = new Cache(httpCacheDirectory, size);
    return new OkHttpClient.Builder()
            .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
            .cache(cache)
            .build();
}

用途:

public static Retrofit getClient(final Context context) {
    if (retrofit == null) {
        retrofit = new Retrofit.Builder()
                .client(getCacheClient(context))
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
    return retrofit;
}

最新更新