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:
- Las imágenes se cargan más rápido en la vista web
- ¿Cómo funciona la caché HTTP de Android?
- Almacenamiento en caché de archivos de guayaba
- Android borrar algunas fotos de caché Picasso
- Necesito descargar varias imágenes al directorio para poder acceder al contenido sin conexión
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
- Cómo Retrofit con OKHttp utilizar datos de caché cuando está fuera de línea
- Transmisión de archivos de audio y almacenamiento en caché
- ¿Cómo se genera la clase con T y List <T>
- ¿Cómo invalidar / forzar la actualización de la ruta de caché en la próxima solicitud con Retrofit y OKHttp?
- Invalidar caché en Picasso
- Datos de servicio web de caché de android
- ¿Qué tipos de caché realiza SQLite en Android?
- Desplazamiento de las imágenes de pantalla en la memoria
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 .
-
En primer lugar, asegúrese de eliminar cualquier caché antiguo. (explorador raíz /data/data/com.yourapp/cache
-
Instanciar el constructor cliente:
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder() .cache(cache) .addInterceptor(new RewriteRequestInterceptor()) .addNetworkInterceptor(new RewriteResponseCacheControlInterceptor())
-
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); } }
-
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 eliminar un repositorio y agregar uno nuevo en Android Studio?
- Una clave de Google Maps para varios desarrolladores (Android)? Eclipse keystore personalizado?