web-dev-qa-db-fra.com

Mise en cache HTTP avec Retrofit 2.0.x

J'essaie de mettre en cache certaines réponses dans mon application à l'aide de Retrofit 2.0, mais il me manque quelque chose.

J'ai installé un fichier de mise en cache comme suit:

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

qui crée un fichier dans /data/user/0/<PackageNmae>/cache/http, puis a préparé un intercepteur de réseau comme suit:

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

mon Retrofit et OkHttpClient instance:

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

ProductsService.class contient:

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

et

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

La question ici est: comment lui faire accéder aux réponses mises en cache lorsque l'appareil est hors ligne?

En-tête de la réponse du 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
17
Amr Barakat

Enfin, j'obtiens la réponse.

L'intercepteur réseau doit être le suivant:

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

puis installer le fichier cache est aussi simple que cela

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());
14
Amr Barakat

Dans votre CachingControlInterceptor, vous créez de nouvelles demandes, mais vous ne les utilisez jamais réellement. Vous appelez newBuilder et ignorez le résultat, donc la modification d'en-tête n'est jamais envoyée où que ce soit. Essayez d'affecter ces valeurs à request puis au lieu d'appeler proceed sur chain.request() appelez-le sur 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();
    }
}
8
iagreen

vous pouvez également essayer:

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

J'ai finalement découvert la solution qui fonctionnait pour moi dans Retrofit 2.x et OkHttp 3.x

J'ai dû implémenter deux intercepteurs, l'un d'eux est chargé de réécrire les en-têtes de demande et l'autre de réécrire les en-têtes de réponse.

  1. Tout d'abord, assurez-vous de supprimer tout ancien cache. (Explorateur racine /data/data/com.yourapp/cache

  2. Instanciez le générateur de client:

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

Il est important de s'assurer que vous ajoutez le ResponseCacheControlInterceptor en tant qu'intercepteur réseau et le RewriteRequestInterceptor en tant qu'intercepteur (comme je l'ai fait lors de la 2e étape).

0
douglas.iacovelli