3

From the answer given for this question Can Retrofit with OKHttp use cache data when offline i was able to come up with this, but the code seems not to cache. What could i be doing wrong?

This my okhttp client

    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();

Add my network interceptor is as below:

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

Am adding to retrofit like this:

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

//Add in my activity:

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

            }
        });

//interface

 @GET("movie/popular")
    Call<MovieResponse> getPopularMoviesDetails(@Query("api_key") String apiKey, @Query("page") int page);
Community
  • 1
  • 1
Derrick Njeru
  • 151
  • 1
  • 9

3 Answers3

2

You shouldn’t rewrite responses from the server to facilitate caching. It’s better to ask your server’s administrators to include cache headers when they serve responses. That way the cache works for all clients – not just OkHttp.

That said, you’re an adult and you’re entitled to take technical shortcuts to avoid talking to your server team.

First you need to change your code to use both a network interceptor and an application interceptor. Why two? Well, when you’re making requests into the cache the network interceptors haven’t run yet. And when you’re writing responses into the cache the application interceptors haven’t run yet.

Your application interceptor will rewrite your request headers to include this when you prefer the cache, and to permit the cache to serve stale responses:

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

Your network interceptor will rewrite your server’s response headers to include this so that all responses are cached for 24 hours:

Cache-Control: max-age=86400
Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
1

Not sure if you already fix this. I found a great/short solution hope it helps.

I create a CacheControlInterceptor. This has 24 hours of cache, if there you are offline and your response is less than 24 hours old you will get the cache response.

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

On this method I create the OKHttpCliente . As you notice I am adding the CacheControlInterceptor and cache.

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

Finally retrofit:

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

Let me know if that works.

Sol
  • 833
  • 1
  • 9
  • 17
0

I was able to resolve the issue.This is how i did it.

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

And usage:

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;
}
Derrick Njeru
  • 151
  • 1
  • 9