Android, Retrofit2, OkHTTP3 and an easy cache implementation

In a recent development, a simple Android App which should get data from a REST API, one of the requirements was:

Cache API requests for 1 hour.

I chose retrofit as my REST Client, and I knew it provided cache mechanisms out of the box (by OkHTTP3).

Following this useful tip, it looked like a long hanging fruit (having a Cache implemented in minutes):

Build a OkHttpClient with:

  • A Cache
  • An interceptor that checks for connectivity and if none asks for cached data

(For that approach, please check that post, I will not stop referring it as it was really helpful. This article continues about what else I needed from the cache..)

So I checked that:

Turned off my network connection (WiFi in a Genymotion VM), and great, it was working, having data without connection (having at least retrieved info once to cache).

But something was off, every time I opened the app, it took the same time to retrieve and show items.. ¿Wasn’t them already cached? Well, let’s see what was happening from the code:

So, in case of no network, we had that check in line 8, which forces the use of the only-if-cached header. In case of network available, we add a Cache-Control header to the request with a given age. So.. what was happening?

After checking the API, I noted that it was returning no Cache-Control header from the server, so I (and maybe you in your case) had some alternatives:

  • Tell the server-side developers if it was a policy to not cache results and solve the conflict analysing the requirement
    • Maybe the same developer is in charge of both mobile and server-side code
    • Maybe it is other developer which we can agree/ask about that change
  • What if there was no way to change it?
    • (Or the server-side was accessible but I decided to asume it was all on my own)… // Don’t do that, ask your fellow developers and avoid the following hack!

After OkHttp code finds the only-if-cached header, it doesn’t attempt to go to the network to fetch data. As it must be. In the network available case (line 9), it all depends on the server-side headers, so having no access to the server code, we needed other kind of interceptor.

Side note: I spent some hardcore debug time to realise that not having cache related headers in my API allowed to save/cache responses (isCacheable method), even though when trying to use, the cached headers didn’t include a Cache-Control header that would have made it possible to use the cache instead of ignoring it (getCandidate method).

Why?

I watched this graph a few seconds:

OkHttp interceptors

Our interceptor is clearly an Application Interceptor, taking the request and adding headers. I knew that I had cached data, but also cached headers which didn’t include the so much needed Cache-Control one, so I had to:

  • Take the response
  • Add the Cache-Control header with the desired value

After those changes, results were obvious, from ~3000ms retrieving from network to ~300ms accessing cached data (values from a Genymotion VM).

Here is a complete code reference

Read more:

rfc7234 – Cache Control

newfivefour – Android: Cache network requests for offline access with Retrofit2 and OkHTTP3

Cache class and ways of setting headers with java

DiskLruCache, one of the most important contributions by Jake Wharton (used by OkHttp among other libraries, frameworks and projects)

Loading Disqus Comments ...
Loading Facebook Comments ...