10

I'm trying to configure cache with Retrofit 1.9.0 and OkHtttp 2.5.0.

Here is how I provide OkHttpClient for my RestAdapter:

@Provides
@Singleton
public OkHttpClient provideOkHttpClient() {
    OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.setConnectTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
    okHttpClient.setReadTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
    okHttpClient.setWriteTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);

    File cacheDir = new File(context.getCacheDir(), "http");
    final Cache cache = new Cache(cacheDir, DISK_CACHE_SIZE_IN_BYTES);
    okHttpClient.setCache(cache);

    okHttpClient.interceptors().add(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {

            Response response = chain.proceed(request);
            Response finalResponse = response.newBuilder()
                    .header("Cache-Control", String.format("public, max-stale=%d", 604800))
                    .build();

            Log.d("OkHttp", finalResponse.toString());
            Log.d("OkHttp Headers", finalResponse.headers().toString());
            return finalResponse;
        }
    });

    return okHttpClient;
}

I did not forget to setClient on RestAdapter.Builder. Also made sure, that I'm actually using instance of RestAdapter with this client set.

Even checked if the files are created under "http" folder. They are.

However after I turn of WIFI and reload my screen I end up in OnError callback of Observable endpoint with this message:

retrofit.RetrofitError: failed to connect to /10.40.31.12 (port 8888) after 10000ms: connect failed: ENETUNREACH (Network is unreachable)

DISCLAIMER: I should probably mention that the final Observable is combined from 5 others, with flatMap and zip on the way.

Jacek Kwiecień
  • 12,397
  • 20
  • 85
  • 157
  • possible duplicate of [Can Retrofit with OKHttp use cache data when offline](http://stackoverflow.com/questions/23429046/can-retrofit-with-okhttp-use-cache-data-when-offline) – njzk2 Sep 16 '15 at 15:59

2 Answers2

4

I think I have an answer. Short one is: "Cannot be done if server sends no-cache header in response".

If you want the longer one, details are below.

I've made a sample app comparing 2 backends. Lets call them Backend A, and Backend B. A was giving me troubles so I've decided to check on B.

A returns CacheControl = "no-cache, no-transform, max-age=0"

B returns Cache-Control = „public" response header

I did the same setup for both backends, just different urls.

    private void buildApi() {
        Gson gson = new GsonBuilder().create();

        OkHttpClient okHttpClient = new OkHttpClient();
        okHttpClient.setConnectTimeout(10, TimeUnit.SECONDS);

        File cacheDir = new File(getCacheDir(), "http");
        final Cache cache = new Cache(cacheDir, 1000000 * 10);
        okHttpClient.setCache(cache);

        okHttpClient.interceptors().add(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                Log.d("OkHttp REQUEST", request.toString());
                Log.d("OkHttp REQUEST Headers", request.headers().toString());

                Response response = chain.proceed(request);
                response = response.newBuilder()
                        .header("Cache-Control", String.format("public, max-age=%d, max-stale=%d", 60, RESPONSE_CACHE_LIFESPAN_IN_SECONDS))
                        .build();

                Log.d("OkHttp RESPONSE", response.toString());
                Log.d("OkHttp RESPONSE Headers", response.headers().toString());
                return response;
            }
        });

        RestAdapter.Builder builder = new RestAdapter.Builder()
                .setConverter(new StringGsonConverter(gson))
                .setClient(new OkClient(okHttpClient))
                .setRequestInterceptor(new RequestInterceptor() {
                    @Override
                    public void intercept(RequestFacade request) {

                        if (isNetworkAvailable()) {
                            request.addHeader("Cache-Control", "public, max-age=" + 60);
                        } else {
                            request.addHeader("Cache-Control", "public, only-if-cached, max-stale=" + RESPONSE_CACHE_LIFESPAN_IN_SECONDS);
                        }
                    }
                });

        builder.setEndpoint("http://this.is.under.vpn.so.wont.work.anyway/api");
        A_API = builder.build().create(AApi.class);

        builder.setEndpoint("http://collector-prod-server.elasticbeanstalk.com/api");
        B_API = builder.build().create(BApi.class);
    }

Did both calls, then disabled wifi. Cache worked fine for B, but A thrown 504 Unsatisfiable Request (only-if-cached)

It seems that overwritting headers won't help in that case.

Jacek Kwiecień
  • 12,397
  • 20
  • 85
  • 157
2

You should rewrite your Request instead of the Response. For reference, see the docs on rewriting requests. Note you, can also use the CacheControl class instead of building your own header if you want. Your interceptor should look something like --

okHttpClient.interceptors().add(new Interceptor() {
  @Override
  public Response intercept(Chain chain) throws IOException {

    Request request = chain.request();
    Request cachedRequest = request.newBuilder()
        .cacheControl(new CacheControl.Builder()
            .maxStale(7, TimeUnit.DAYS)
            .build())
        .build();

    return chain.proceed(cachedRequest);
  }
});
iagreen
  • 31,470
  • 8
  • 76
  • 90