Caching HTTP con Retrofit 2.0.x

Sto cercando di memorizzare alcune risposte nella mia app usando Retrofit 2.0, ma mi manca qualcosa.

Ho installato un file di memorizzazione nella cache come segue:

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

che crea un file in /data/user/0//cache/http , quindi preparato un intercettore di rete come segue:

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

la mia istanza di Retrofit e 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); 

dove ProductsService.class contiene:

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

e

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

La domanda qui è: come fare ad accedere alle risposte in cache quando il dispositivo è offline?

L’intestazione della risposta 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 ho la risposta.

Network Interceptor dovrebbe essere come segue:

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

quindi l’installazione del file di cache è così semplice

 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()); 

In CachingControlInterceptor , crei nuove richieste, ma non le usi mai. Chiama newBuilder e ignori il risultato, quindi la modifica dell’intestazione non viene mai effettivamente inviata dove. Prova a assegnare quei valori alla request e poi invece di chiamare proceed su chain.request() chiamalo su 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(); } } 

puoi anche provare:

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

Alla fine ho scoperto la soluzione che ha funzionato per me in Retrofit 2.xe OkHttp 3.x

Ho dovuto implementare due Interceptor , uno dei quali è responsabile di riscrivere le intestazioni Request e l’altro di riscrivere le intestazioni Response .

  1. Innanzitutto, assicurati di eliminare qualsiasi cache precedente. (root explorer /data/data/com.yourapp/cache

  2. Creare un’istanza del builder client:

     OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder() .cache(cache) .addInterceptor(new RewriteRequestInterceptor()) .addNetworkInterceptor(new RewriteResponseCacheControlInterceptor()) 
  3. Creare il 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. Creare il 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(); } } 

È importante assicurarsi di aggiungere ResponseCacheControlInterceptor come Network Interceptor e RewriteRequestInterceptor come Interceptor (come ho fatto nel 2 ° passaggio).

  OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();