Retrofit OKHTTP Offline Caching Not Working
Solution 1:
June 2021 (Retrofit 2.9.0 or OKHTTP 3.14.9) Complete Solution (Update)
Same approach is still working since: Oct. 2018
Oct. 2018 (Retrofit 2.4 or OKHTTP 3.11) Complete Solution
Ok, so Online & Offline caching using OKHTTP or Retrofit has been causing so many problems for many people on stackoverflow and other forums. There are tons of misleading information and non-working code samples all over the internet.
So, today I will explain how you can implement online & offline caching using Retrofit & OKHTTP with clear steps + How to test and know whether you are getting the data from cache or network.
If you are getting a 504 Unsatisfiable Request (only-if-cached)
OR an Unable to resolve host "HOST": No address associated with hostname
then you can use any of the following solutions.
Before you begin, you must always remember to:
- Make sure you are using a GET request and not a POST!
- Always make sure you add
.removeHeader("Pragma")
as shown below (This lets you override the server's caching protocol) - Avoid using the HttpLoggingInterceptor while testing, it can cause some confusion in the beginning. Enable it in the end if you want.
- ALWAYS ALWAYS ALWAYS delete your app from the device and reinstall it again upon every change in code, if you want to explore using Interceptors. Otherwise changing code while the old cache data is still on the device will cause you lots of confusion and misleading deductions!
- The order of adding Interceptors to OKHTTPClient object matters!
N.B: If you want to depend on your server's caching protocol for online and offline caching, then don't read the 2 solutions. Just read this article. All you need is to create a cache object and attache it to OKHTTPClient object.
Solution 1: (Longer, but you have full control)
Step 1: (Create onlineInterceptor)
static Interceptor onlineInterceptor = new Interceptor() { @Override public okhttp3.Response intercept(Chain chain) throws IOException { okhttp3.Response response = chain.proceed(chain.request()); int maxAge = 60; // read from cache for 60 seconds even if there is internet connection return response.newBuilder() .header("Cache-Control", "public, max-age=" + maxAge) .removeHeader("Pragma") .build(); } };
Step 2: (Create Offline Interceptor) (Only if you want cache access when offline)
static Interceptor offlineInterceptor= new Interceptor() { @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request request = chain.request(); if (!isInternetAvailable()) { int maxStale = 60 * 60 * 24 * 30; // Offline cache available for 30 days request = request.newBuilder() .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) .removeHeader("Pragma") .build(); } return chain.proceed(request); } };
Step 3: (Create a cache object)
int cacheSize = 10 * 1024 * 1024; // 10 MB Cache cache = new Cache(context.getCacheDir(), cacheSize);
Step 4: (Add interceptors and cache to an OKHTTPClient object)
OkHttpClient okHttpClient = new OkHttpClient.Builder() // .addInterceptor(provideHttpLoggingInterceptor()) // For HTTP request & Response data logging .addInterceptor(OFFLINE_INTERCEPTOR) .addNetworkInterceptor(ONLINE_INTERCEPTOR) .cache(cache) .build();
Step 5:(If you are using Retrofit, add the OKHTTPClient object to it)
retrofit = new retrofit2.Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(okHttpClient) .build();
DONE!
Solution 2: (Just use a library to do all that for you! But deal with the limitations)
Use OkCacheControl library
Step 1 (Create Cache object as shown above)
Step 2 (Create an OKHTTPClient object)
OkHttpClient okHttpClient = OkCacheControl.on(new OkHttpClient.Builder()) .overrideServerCachePolicy(1, MINUTES) .forceCacheWhenOffline(networkMonitor) .apply() // return to the OkHttpClient.Builder instance //.addInterceptor(provideHttpLoggingInterceptor()) .cache(cache) .build();
Step 3:(Attach the OKHTTPClient object to Retrofit as shown above)
Step 4: (Create a NetworkMonitor Object)
static OkCacheControl.NetworkMonitor networkMonitor=new OkCacheControl.NetworkMonitor() { @Override public boolean isOnline() { return isInternetAvailable(); } };
DONE!
Testing:
In order to know whether your device is getting data from the network or from cache, simply add the following code to your onResponse
method of Retrofit.
public void onResponse(Call<List<RetroPhoto>> call, Response<List<RetroPhoto>> response) {
if (response.raw().cacheResponse() != null) {
Log.e("Network", "response came from cache");
}
if (response.raw().networkResponse() != null) {
Log.e("Network", "response came from server");
}
}
If the device is using the Network, you will get "response came from server".
If device is using Cache, you will get both of the above responses! For more info about this read this article.
For more info about using OKHTTP interceptors go to this page.
Post a Comment for "Retrofit OKHTTP Offline Caching Not Working"