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

时间:2016-07-24 13:11:49

标签: retrofit okhttp

从这个问题的答案Can Retrofit with OKHttp use cache data when offline我能够想出这个,但代码似乎没有缓存。我能做错什么?

这是我的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);

3 个答案:

答案 0 :(得分:2)

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

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

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

您的应用程序拦截器将重写您的请求标头,以便在您更喜欢缓存时包含此标头,并允许缓存提供过时的响应:

GET http://localhost:54/i18n/en.json 404 (Not Found)

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

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

答案 1 :(得分:1)

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

我创建了一个 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();

如果有效,请告诉我。

答案 2 :(得分:0)

我能够解决这个问题。这就是我做到的。

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