Retrofit OKHTTP Offline caching not working

前端 未结 1 827
攒了一身酷
攒了一身酷 2021-02-03 13:02

I read dozens of tutorial and Stackoverflow answers to my problem but nothing is working for me! Also, most of them are old so probably OKHTTP changed somehow.

All I wan

1条回答
  •  孤街浪徒
    2021-02-03 13:16

    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 hostnamethen 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> call, Response> 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.

    0 讨论(0)
提交回复
热议问题