15
votes

I want to Retrofit with OkHttp uses cache when is no Internet.

I prepare OkHttpClient like this:

    RestAdapter.Builder builder= new RestAdapter.Builder()
       .setRequestInterceptor(new RequestInterceptor() {
            @Override
            public void intercept(RequestFacade request) {
                request.addHeader("Accept", "application/json;versions=1");
                if (MyApplicationUtils.isNetworkAvaliable(context)) {
                    int maxAge = 60; // read from cache for 1 minute
                    request.addHeader("Cache-Control", "public, max-age=" + maxAge);
                } else {
                    int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
                    request.addHeader("Cache-Control", 
                        "public, only-if-cached, max-stale=" + maxStale);
                }
            }
    });

and setting cache like this:

   Cache cache = null;
    try {
        cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
    } catch (IOException e) {
        Log.e("OKHttp", "Could not create http cache", e);
    }

    OkHttpClient okHttpClient = new OkHttpClient();
    if (cache != null) {
        okHttpClient.setCache(cache);
    }

and I checked on rooted device, that in cache directory are saving files with the "Response headers" and Gzip files.

But I don't get the correct answer from retrofit cache in offline, although in GZip file is coded my correct answer. So how can I make Retrofit can read GZip file and how can he know which file it should be (because I have a few files there with other responses) ?

1

1 Answers

25
votes

I have simlar problem in my company :)

The problem was on server side. In serwer response i have:

Pragma: no-cache

So when i removed this everything starts working. Before i removed it i get all the time such exceptions: 504 Unsatisfiable Request (only-if-cached)

Ok so how implementation on my side looks.

    OkHttpClient okHttpClient = new OkHttpClient();

    File httpCacheDirectory = new File(appContext.getCacheDir(), "responses");

    Cache cache = new Cache(httpCacheDirectory, maxSizeInBytes);
    okHttpClient.setCache(cache);

    OkClient okClient = new OkClient(okHttpClient);

    RestAdapter.Builder builder = new RestAdapter.Builder();
    builder.setEndpoint(endpoint);
    builder.setClient(okClient);

If you have problems in testing on which side is problem (server or app). You can use such feauture to set headers received from server.

private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());
        return originalResponse.newBuilder()
                               .removeHeader("Pragma")
                               .header("Cache-Control",
                                       String.format("max-age=%d", 60))
                               .build();
    }
};

and simply add it:

okHttpClient.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR);

Thanks to that as you can see i was able to remove Pragma: no-cache header for test time.

Also i suggest you to read about Cache-Control header:

max-age,max-stale

Other usefull links:

List of HTTP header fields

Cache controll

Another sample code