Caché HTTP con Retrofit 2.0.x

Estoy tratando de caché algunas respuestas en mi aplicación utilizando Retrofit 2.0, pero me falta algo.

He instalado un archivo de caché de la siguiente manera:

private static File httpCacheDir; private static Cache cache; try { httpCacheDir = new File(getApplicationContext().getCacheDir(), "http"); httpCacheDir.setReadable(true); long httpCacheSize = 10 * 1024 * 1024; // 10 MiB HttpResponseCache.install(httpCacheDir, httpCacheSize); cache = new Cache(httpCacheDir, httpCacheSize); Log.i("HTTP Caching", "HTTP response cache installation success"); } catch (IOException e) { Log.i("HTTP Caching", "HTTP response cache installation failed:" + e); } public static Cache getCache() { return cache; } 

que crea un archivo en /data/user/0/<PackageNmae>/cache/http , entonces preparó un interceptor de red de la siguiente manera:

 public class CachingControlInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // Add Cache Control only for GET methods if (request.method().equals("GET")) { if (ConnectivityUtil.checkConnectivity(getContext())) { // 1 day request.newBuilder() .header("Cache-Control", "only-if-cached") .build(); } else { // 4 weeks stale request.newBuilder() .header("Cache-Control", "public, max-stale=2419200") .build(); } } Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .header("Cache-Control", "max-age=86400") .build(); } } 

mi instancia de Retrofit y OkHttpClient :

 OkHttpClient client = new OkHttpClient(); client.setCache(getCache()); client.interceptors().add(new MainInterceptor()); client.interceptors().add(new LoggingInceptor()); client.networkInterceptors().add(new CachingControlInterceptor()); Retrofit restAdapter = new Retrofit.Builder() .client(client) .baseUrl(Constants.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); productsService = restAdapter.create(ProductsService.class); 

donde ProductsService.class contiene:

 @Headers("Cache-Control: max-age=86400") @GET("categories/") Call<PagedResponse<Category>> listCategories(); 

y

 Call<PagedResponse<Category>> call = getRestClient().getProductsService().listCategories(); call.enqueue(new GenericCallback<PagedResponse<Category>>() { // whatever // GenericCallback<T> implements Callback<T> } }); 

La pregunta aquí es: ¿Cómo hacer que el acceso a las respuestas en caché cuando el dispositivo está fuera de línea?

Encabezado de la respuesta del backend:

 Allow → GET, HEAD, OPTIONS Cache-Control → max-age=86400, must-revalidate Connection → keep-alive Content-Encoding → gzip Content-Language → en Content-Type → application/json; charset=utf-8 Date → Thu, 17 Dec 2015 09:42:49 GMT Server → nginx Transfer-Encoding → chunked Vary → Accept-Encoding, Cookie, Accept-Language X-Frame-Options → SAMEORIGIN x-content-type-options → nosniff x-xss-protection → 1; mode=block 

Finalmente consigo la respuesta.

Interceptor de red debe ser como sigue:

 public class CachingControlInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // Add Cache Control only for GET methods if (request.method().equals("GET")) { if (ConnectivityUtil.checkConnectivity(YaootaApplication.getContext())) { // 1 day request = request.newBuilder() .header("Cache-Control", "only-if-cached") .build(); } else { // 4 weeks stale request = request.newBuilder() .header("Cache-Control", "public, max-stale=2419200") .build(); } } Response originalResponse = chain.proceed(request); return originalResponse.newBuilder() .header("Cache-Control", "max-age=600") .build(); } } 

entonces instalar el archivo de caché es tan simple

 long SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MiB Cache cache = new Cache(new File(context.getCacheDir(), "http"), SIZE_OF_CACHE); OkHttpClient client = new OkHttpClient(); client.cache(cache); client.networkInterceptors().add(new CachingControlInterceptor()); 

En su CachingControlInterceptor , crea nuevas solicitudes, pero nunca las usa. Usted llama a newBuilder e ignora el resultado, por lo que la modificación de encabezado nunca se envía realmente a ninguna parte. Trate de asignar esos valores para request y luego en lugar de llamar a proceed on chain.request() llamarlo a request .

 public class CachingControlInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // Add Cache Control only for GET methods if (request.method().equals("GET")) { if (ConnectivityUtil.checkConnectivity(getContext())) { // 1 day request = request.newBuilder() .header("Cache-Control", "only-if-cached") .build(); } else { // 4 weeks stale request = request.newBuilder() .header("Cache-Control", "public, max-stale=2419200") .build(); } } Response originalResponse = chain.proceed(request); return originalResponse.newBuilder() .header("Cache-Control", "max-age=600") .build(); } } 

también puede probar:

 public class CachingInterceptor implements Interceptor { @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request request = chain.request(); request = new Request.Builder() .cacheControl(new CacheControl.Builder() .maxAge(1, TimeUnit.DAYS) .minFresh(4, TimeUnit.HOURS) .maxStale(8, TimeUnit.HOURS) .build()) .url(request.url()) .build(); return chain.proceed(request); } } 

Finalmente descubrí la solución que funcionó para mí en Retrofit 2.x y OkHttp 3.x

Tuve que implementar dos Interceptores , uno de ellos es responsable de reescribir los encabezados de la Solicitud y el otro para volver a escribir los encabezados de la Respuesta .

  1. En primer lugar, asegúrese de eliminar cualquier caché antiguo. (explorador raíz /data/data/com.yourapp/cache

  2. Instanciar el constructor cliente:

     OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder() .cache(cache) .addInterceptor(new RewriteRequestInterceptor()) .addNetworkInterceptor(new RewriteResponseCacheControlInterceptor()) 
  3. Cree el RewriteRequestInterceptor

     public class RewriteRequestInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { int maxStale = 60 * 60 * 24 * 5; Request request; if (NetworkUtils.isNetworkAvailable()) { request = chain.request(); } else { request = chain.request().newBuilder().header("Cache-Control", "max-stale=" + maxStale).build(); } return chain.proceed(request); } } 
  4. Cree el RewriteResponseCacheControlInterceptor

     public class RewriteResponseCacheControlInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { int maxStale = 60 * 60 * 24 * 5; Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder().header("Cache-Control", "public, max-age=120, max-stale=" + maxStale).build(); } } 

Es importante asegurarse de que agrega el ResponseCacheControlInterceptor como Interceptor de red y el RewriteRequestInterceptor como Interceptor (como lo hice en el paso 2).

  OkHttpClient client = new OkHttpClient.Builder().cache(cache).build(); 
  • ¿Cómo configuro correctamente Volley para descargar imágenes desde una URL
  • Android: Estrategia de caché de imágenes y tamaño de caché de memoria
  • Memoria caché de picasso para Android
  • Android Volley + JSONObjectRequest Almacenamiento en caché
  • ¿Cómo saber OkHttpClient para ignorar la caché y forzar la actualización desde el servidor?
  • Android: Guardar imágenes en la base de datos Sqlite
  • Cómo eliminar la caché de aplicaciones para todas las aplicaciones en Android M?
  • Obtener imagen uri de picasso?
  • RxJava Android: cargar datos de caché en los subprocesos adecuados
  • Eliminar archivos almacenados en caché - WebView Android 4.4+
  • Almacenamiento en caché de volcado y mapa de bits
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.