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:
(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).
I watched this graph a few seconds:
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
DiskLruCache, one of the most important contributions by Jake Wharton (used by OkHttp among other libraries, frameworks and projects)