代码之家  ›  专栏  ›  技术社区  ›  Mena

改装OKHTTP脱机缓存不工作

  •  1
  • Mena  · 技术社区  · 6 年前

    我读了很多教程和StackOverflow对我的问题的答案,但是没有什么对我有用!而且,它们中的大多数都是旧的,所以OKHTTP可能以某种方式发生了变化。

    我只想启用 脱机缓存 用于改装。

    我用的是GET

    我只想用 offlineCacheInterceptor 作为一个拦截器,但我不断得到:

    Unable to resolve host "jsonplaceholder.typicode.com": No address associated with hostname
    

    我试着结合使用 脱机缓存拦截器 作为拦截器+ provideCacheInterceptor() 作为一个网络拦截器,但我不断得到:

    504 Unsatisfiable Request (only-if-cached) and a null response.body()
    

    我甚至确定要补充 .removeHeader("Pragma") 到处都是!


    我尝试了所有这些链接:

    https://newfivefour.com/android-retrofit2-okhttp3-cache-network-request-offline.html (一个拦截器,不工作!!)

    https://medium.com/mindorks/caching-with-retrofit-store-responses-offline-71439ed32fda (一个拦截器,不工作!)

    https://caster.io/lessons/retrofit-2-offline-cache (单独在线+离线缓存,不工作)

    https://www.journaldev.com/23297/android-retrofit-okhttp-offline-caching (不工作,504个不可满足的请求(仅当缓存时)

    http://mikescamell.com/gotcha-when-offline-caching-with-okhttp3/ (一个拦截器,不工作!!)

    https://stackoverflow.com/a/48295397/8086424 (不工作) 无法解析主机“jsonplaceholder.typicode.com”:没有与主机名关联的地址

    Can Retrofit with OKHttp use cache data when offline (太混乱了!)


    这是我的代码:

    public static Retrofit getRetrofitInstance(Context context) {
            if (retrofit == null) {
                c = context;
                int cacheSize = 10 * 1024 * 1024; // 10 MB
                Cache cache = new Cache(context.getCacheDir(), cacheSize);
                OkHttpClient okHttpClient = new OkHttpClient.Builder()
                        .addInterceptor(provideHttpLoggingInterceptor())
                        .addInterceptor(offlineCacheInterceptor)
                        .addNetworkInterceptor(provideCacheInterceptor())
                        .cache(cache)
                        .build();
                //////////////////////////
                retrofit = new retrofit2.Retrofit.Builder()
                        .baseUrl(BASE_URL)
                        .addConverterFactory(GsonConverterFactory.create())
                        .client(okHttpClient)
                        .build();
            }
            return retrofit;
        }
    

     public static Interceptor offlineCacheInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                Log.e("bbbb", "bbbb");
                if (!checkInternetAvailability()) {
                    Log.e("aaaaa", "aaaaaa");
                    CacheControl cacheControl = new CacheControl.Builder()
                            .maxStale(30, TimeUnit.DAYS)
                            .build();
    
                    request = request.newBuilder()
                            .cacheControl(cacheControl)
                            .removeHeader("Pragma")
                            .build();
                }
                return chain.proceed(request);
            }
        };
    

     public static Interceptor provideCacheInterceptor() {
            return new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Response response = chain.proceed(chain.request());
    
                    // re-write response header to force use of cache
                    CacheControl cacheControl = new CacheControl.Builder()
                            .maxAge(2, TimeUnit.MINUTES)
                            .build();
    
                    return response.newBuilder()
                            .header(CACHE_CONTROL, cacheControl.toString())
                            .removeHeader("Pragma")
                            .build();
                }
            };
        }
    

    我正在使用jsonplaceholder.typicode.com/photos返回:

    content-type: application/json; charset=utf-8
        date: Sun, 21 Oct 2018 14:26:41 GMT
        set-cookie: __cfduid=d9e935012d2f789245b1e2599a41e47511540132001; expires=Mon, 21-Oct-19 14:26:41 GMT; path=/; domain=.typicode.com; HttpOnly
        x-powered-by: Express
        vary: Origin, Accept-Encoding
        access-control-allow-credentials: true
        expires: Sun, 21 Oct 2018 18:26:41 GMT
        x-content-type-options: nosniff
        etag: W/"105970-HCYFejK2YCxztz8++2rHnutkPOQ"
        via: 1.1 vegur
        cf-cache-status: REVALIDATED
        expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
        server: cloudflare
        cf-ray: 46d466910cab3d77-MXP
        Cache-Control: public, max-age=60
    
    1 回复  |  直到 6 年前
        1
  •  9
  •   Mena    6 年前

    2018年10月(改装2.4或OKHTTP 3.11)完整解决方案

    好吧,所以使用OKHTTP或改型的在线和离线缓存给StackOverflow和其他论坛上的许多人带来了很多问题。互联网上有大量误导性信息和非工作代码样本。

    因此,今天我将解释如何使用revent&okhttp实现在线和离线缓存,并提供清晰的步骤+如何测试和知道您是从缓存还是网络获取数据。

    如果你得到 504 Unsatisfiable Request (only-if-cached) Unable to resolve host "HOST": No address associated with hostname 然后您可以使用以下任何解决方案。

    开始之前,您必须始终记住:

    • 确保您使用的是GET请求,而不是POST!
    • 始终确保添加 .removeHeader("Pragma") 如下所示(这样可以覆盖服务器的缓存协议)
    • 避免在测试时使用httploggingInterceptor,它会在开始时引起一些混乱。如果需要的话,最后启用它。
    • 如果您想使用拦截器进行探索,请始终从设备中删除您的应用程序,并在每次代码更改时重新安装。否则,在旧的缓存数据仍在设备上时更改代码将导致许多混淆和误导性扣减!
    • 向okhttpclient对象添加拦截器的顺序很重要!

    注意:如果您想依赖服务器的缓存协议进行在线和离线缓存,那么不要阅读这两个解决方案。读一下这个 article . 您所需要的只是创建一个缓存对象并将其附加到okhttpclient对象。


    解决方案1: (更长,但你完全控制)

    • 步骤1:(创建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();
          }
      };
      
    • 步骤2:(创建脱机拦截器)(仅当您希望在脱机时访问缓存时)

         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);
         }
       };
      
    • 步骤3:(创建缓存对象)

      int cacheSize = 10 * 1024 * 1024; // 10 MB
      Cache cache = new Cache(context.getCacheDir(), cacheSize);
      
    • 步骤4:(向okhttpclient对象添加拦截器和缓存)

          OkHttpClient okHttpClient = new OkHttpClient.Builder()
       // .addInterceptor(provideHttpLoggingInterceptor()) // For HTTP request & Response data logging
          .addInterceptor(OFFLINE_INTERCEPTOR)
          .addNetworkInterceptor(ONLINE_INTERCEPTOR)
          .cache(cache)
          .build();
      
    • 步骤5:(如果您正在使用remodify,请将okhttpclient对象添加到其中)

               retrofit = new retrofit2.Retrofit.Builder()
              .baseUrl(BASE_URL)
              .addConverterFactory(GsonConverterFactory.create())
              .client(okHttpClient)
              .build();
      

    完成!


    解决方案2: (用图书馆为你做所有这些事吧!但要处理这些局限性)

    使用 OkCacheControl 图书馆

    • 步骤1(如上图所示创建缓存对象)
    • 步骤2(创建okhttpclient对象)

           OkHttpClient okHttpClient = OkCacheControl.on(new OkHttpClient.Builder())
           .overrideServerCachePolicy(1, MINUTES)
           .forceCacheWhenOffline(networkMonitor)
           .apply() // return to the OkHttpClient.Builder instance
         //.addInterceptor(provideHttpLoggingInterceptor())
           .cache(cache)
           .build();
      
    • 步骤3:(如上图所示,将okhttpclient对象附加到改造中)

    • 步骤4:(创建NetworkMonitor对象)

         static OkCacheControl.NetworkMonitor networkMonitor=new 
         OkCacheControl.NetworkMonitor() {
         @Override
          public boolean isOnline() {
          return isInternetAvailable();
         }
        };
      

    完成!


    测试: 为了知道设备是从网络还是从缓存获取数据,只需将以下代码添加到 onResponse 改造方法。

     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");
                }
            }
    

    如果设备正在使用网络,您将得到“来自服务器的响应”。

    如果设备正在使用缓存,您将获得上述两个响应!有关此的详细信息,请阅读此 article .


    有关使用OKHTTP拦截器的详细信息,请转到 page .