Caching with OkHttp Interceptor and Retrofit

Authors
  • Amit Shekhar
    Name
    Amit Shekhar
    Published on
Caching with OkHttp Interceptor and Retrofit

I am Amit Shekhar, Co-Founder @ Outcome School, I have taught and mentored many developers, and their efforts landed them high-paying tech jobs, helped many tech companies in solving their unique problems, and created many open-source libraries being used by top companies. I am passionate about sharing knowledge through open-source, blogs, and videos.

Join Outcome School and get high paying tech job: Outcome School

Before we start, I would like to mention that, I have released a video playlist to help you crack the Android Interview: Check out Android Interview Questions and Answers.

In this blog, we are going to learn how to cache HTTP responses in Android using OkHttp Interceptor and Retrofit for building offline-first Android apps.

Read more about the OkHttp Interceptor: OkHttp Interceptor

Let's understand how caching is going to help us in our Android applications.

  • When we make a network call to fetch the data from the server.
  • For the very first time, it will get the data from the server and it will cache the HTTP response on the client.
  • Then, if we make the same API call again, it will return the data from the cache instantly.

This way, our Android applications can do two very important things:

  • Work even if there is no internet connection. This will helps us in building the offline-first apps.
  • Work faster as the response is cached locally.

Now, let's learn how to enable caching in OkHttp and Retrofit. Before that, we need to understand that Retrofit uses the OkHttp client for HTTP operations, which means that whatever we have to do to enable caching, we need to do with the OkHttp. We do not have to do anything extra for Retrofit as it will use our configured OkHttp client.

Things become easier when we already have the Cache-Control header enabled from the server, then OkHttp will respect that header and cache the response for a specific time that is being sent from the server.

But what if the Cache-Control is not enabled from the server? We can still cache the response from OkHttp Client using Interceptor. We will learn how.

For this, we need to implement Interceptor like below:

class CacheInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val response: Response = chain.proceed(chain.request())
        val cacheControl = CacheControl.Builder()
            .maxAge(10, TimeUnit.DAYS)
            .build()
        return response.newBuilder()
            .header("Cache-Control", cacheControl.toString())
            .build()
    }
}

Here, we created CacheInterceptor by implementing Interceptor and we have a CacheControl builder that is used to provide the header for the Cache-Control.

Then, we need to add this CacheInterceptor to the OkHttpClient using addNetworkInterceptor.

val okHttpClient = OkHttpClient().newBuilder()
.cache(Cache(File(applicationContext.cacheDir, "http-cache"), 10L * 1024L * 1024L)) // 10 MiB
.addNetworkInterceptor(CacheInterceptor())
.build();

After that, we can use this okHttpClient directly or with Retrofit.

But, there is a catch that we need to understand while building the offline-first app.

OkHttp is designed in such a way that it returns the cached response only when the Internet is available.

  • It returns the data from the cache only when the Internet is available and the data is cached.
  • It returns with the error "no internet available" even when the data is cached and but the Internet is not available.

How to solve this issue?

We can create a ForceCacheInterceptor in addition to the above one (CacheInterceptor, only if Cache-Control header is not enabled from the server).

class ForceCacheInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val builder: Request.Builder = chain.request().newBuilder()
        if (!IsInternetAvailable()) {
            builder.cacheControl(CacheControl.FORCE_CACHE);
        }
        return chain.proceed(builder.build());
    }
}

Then, add the interceptor in OkHttpClient like below:

val okHttpClient = OkHttpClient().newBuilder()
.cache(Cache(File(applicationContext.cacheDir, "http-cache"), 10L * 1024L * 1024L)) // 10 MiB
.addNetworkInterceptor(CacheInterceptor()) // only if Cache-Control header is not enabled from the server
.addInterceptor(ForceCacheInterceptor())
.build();

Here, we need to notice that we are adding ForceCacheInterceptor to OkHttpClient using addInterceptor() and not addNetworkInterceptor().

  • addInterceptor: used to add the interceptor at the application level.
  • addNetworkInterceptor: As the name says, used to add the interceptor at the network level.

After that, we can use this okHttpClient directly or with Retrofit and it will work as expected.

This is how we can cache HTTP responses in Android using OkHttp Interceptor and Retrofit for building offline-first Android apps.

Prepare yourself for Android Interview: Android Interview Questions

That's it for now.

Thanks

Amit Shekhar
Co-Founder @ Outcome School

You can connect with me on:

Follow Outcome School on:

Read all of our high-quality blogs here.