2015-04-27 1 views
3

Je sais qu'il y a eu beaucoup de questions similaires, mais je les ai toutes lues et aucune d'elles n'a vraiment aidé.Retrofit + OkHTTP - le cache de réponse ne fonctionne pas

Donc, voici mon problème:

J'utilise retrofit + okhttp pour récupérer des données de l'API et je voudrais les mettre en cache. Malheureusement, je n'ai pas d'accès admin au serveur API, donc je ne peux pas modifier les en-têtes renvoyés par le serveur. (actuellement, le serveur retourne Cache-control: private)

J'ai donc décidé d'utiliser la falsification d'en-tête okhttp pour insérer des en-têtes de cache appropriés. Malheureusement, peu importe ce que je fais, la mise en cache ne semble pas fonctionner.

J'Initialise le service api comme ceci:

int cacheSize = 10 * 1024 * 1024; // 10 MiB 
File cacheFile = new File(context.getCacheDir(), "thumbs"); 
final Cache cache = new Cache(cacheFile, cacheSize); 

OkHttpClient client = new OkHttpClient(); 
client.setCache(cache); 
client.interceptors().add(new Interceptor() { 
    @Override 
    public Response intercept(Chain chain) throws IOException { 
     Response originalResponse = chain.proceed(chain.request()); 
     return originalResponse.newBuilder() 
       .removeHeader("Access-Control-Allow-Origin") 
       .removeHeader("Vary") 
       .removeHeader("Age") 
       .removeHeader("Via") 
       .removeHeader("C3-Request") 
       .removeHeader("C3-Domain") 
       .removeHeader("C3-Date") 
       .removeHeader("C3-Hostname") 
       .removeHeader("C3-Cache-Control") 
       .removeHeader("X-Varnish-back") 
       .removeHeader("X-Varnish") 
       .removeHeader("X-Cache") 
       .removeHeader("X-Cache-Hit") 
       .removeHeader("X-Varnish-front") 
       .removeHeader("Connection") 
       .removeHeader("Accept-Ranges") 
       .removeHeader("Transfer-Encoding") 
       .header("Cache-Control", "public, max-age=60") 
       //.header("Expires", "Mon, 27 Apr 2015 08:15:14 GMT") 
       .build(); 
    } 
}); 

RestAdapter restAdapter = new RestAdapter.Builder() 
    .setEndpoint(API_ROOT) 
    .setLogLevel(RestAdapter.LogLevel.HEADERS_AND_ARGS) 
    .setClient(new OkClient(client)) 
    .setConverter(new SimpleXMLConverter(false)) 
    .setRequestInterceptor(new RequestInterceptor() { 
     @Override 
     public void intercept(RequestFacade request) { 
      if (Network.isConnected(context)) { 
       int maxAge = 60; // read from cache for 2 minutes 
       request.addHeader("Cache-Control", "public, max-age=" + maxAge); 
      } else { 
       int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale 
       request.addHeader("Cache-Control", 
        "public, only-if-cached, max-stale=" + maxStale); 
      } 
     } 
    }) 
    .build(); 
api = restAdapter.create(ApiService.class); 

Bien sûr, il n'est pas nécessaire d'enlever tous ces en-têtes, mais je voulais faire la réponse aussi propre que possible pour écarter des interférences de ces supplémentaires en-têtes. Comme vous pouvez le voir, j'ai aussi essayé d'usurper l'en-tête Expires et Date (j'ai essayé de les supprimer, en les réglant de manière à ce qu'il y ait exactement la différence entre eux et en définissant Expires longtemps dans le futur). J'ai également expérimenté diverses valeurs de contrôle du cache, mais pas de chance.

J'ai vérifié que le fichier cacheFile existe, isDirectory et est accessible en écriture pour l'application.

Ce sont les en-têtes de requête et de réponse en direct par retrofit connecté:

Request: 
Cache-Control: public, max-age=60 
---> END HTTP (no body) 

Response: 
Date: Mon, 27 Apr 2015 08:41:10 GMT 
Server: Apache/2.2.22 (Ubuntu) 
Expires: Mon, 27 Apr 2015 08:46:10 GMT 
Content-Type: text/xml; charset=UTF-8 
OkHttp-Selected-Protocol: http/1.1 
OkHttp-Sent-Millis: 1430124070000 
OkHttp-Received-Millis: 1430124070040 
Cache-Control: public, max-age=60 
<--- END HTTP (-1-byte body) 
<--- BODY: ... 

Et, enfin un incident étrange: à un moment donné, le cache a travaillé pendant quelques minutes. Je recevais un nombre de hits raisonnable, même les requêtes hors ligne renvoyaient des valeurs mises en cache. (C'est arrivé en utilisant le réglage exact affiché ici) Mais quand j'ai redémarré l'application, tout était redevenu "normal" (nombre de coups constant 0).

Co si quelqu'un a une idée de ce que pourrait être le problème ici, je serais vraiment heureux pour toute aide :)

Répondre

8

Utilisez networkInterceptors() au lieu d'intercepteurs(). Cela en combinaison avec votre stratégie de suppression des en-têtes qui sont en quelque sorte liés à la mise en cache fonctionnera. C'est la réponse courte. Lorsque vous utilisez des intercepteurs pour modifier les en-têtes, aucun ajustement n'est effectué avant l'appel de CacheStrategy.isCacheable(). Il est intéressant de regarder les classes CacheStrategy et CacheControl pour voir comment OKHttp gère les en-têtes liés au cache. Il est également intéressant de faire ctrl + f "cache" sur http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Je ne suis pas sûr si la documentation networkInterceptors() et interceptors() est juste floue ou s'il y a un bug. Une fois que je me pencherai sur ce point, je mettrai à jour cette réponse.

+1

Oh mon garçon, stupide moi. Merci beaucoup. Je dois avoir une sorte de cécité temporelle ou quelque chose - je n'ai jamais réalisé qu'il y a des intercepteurs() et pas networkInterceptors() dans ce code ... – daemontus

7

Encore une chose à ajouter ici, Mis à part la réponse de Brendan Weinstein, juste pour confirmer que le cache OkHttp3 ne fonctionnera pas avec les requêtes postées.