【问题标题】:HTTP Caching with Retrofit 2.0.x使用 Retrofit 2.0.x 进行 HTTP 缓存
【发布时间】:2016-03-22 12:46:44
【问题描述】:

我正在尝试使用 Retrofit 2.0 在我的应用中缓存一些响应,但我遗漏了一些东西。

我安装了一个缓存文件如下:

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

/data/user/0/<PackageNmae>/cache/http 中创建一个文件 ,然后准备一个网络拦截器如下:

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

我的RetrofitOkHttpClient 实例:

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 包含:

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

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

这里的问题是:如何让它在设备离线时访问缓存的响应?

后端响应的标头是:

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

【问题讨论】:

    标签: android caching retrofit okhttp


    【解决方案1】:

    在您的CachingControlInterceptor 中,您创建新请求,但从未实际使用它们。您调用newBuilder 并忽略结果,因此标头修改实际上永远不会发送到任何地方。尝试将这些值分配给request,然后不要在chain.request() 上调用proceed,而是在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();
        }
    }
    

    【讨论】:

    • 你能解释一下最后一部分的作用吗? return originalResponse.newBuilder() .header("Cache-Control", "max-age=600") .build();
    • 我认为如果你添加一个标题("Cache-Control", "only-if-cached"),它只会在缓存时检索它。否则,它甚至不会费心发出网络请求。
    • @iagreen 是否可以使用“发布”方法?
    【解决方案2】:

    我终于得到了答案。

    网络拦截器应该如下:

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

    那么安装缓存文件就这么简单

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

    【讨论】:

    • okhttp3.OkHttpClient的setCache方法不可用client.setCache(cache);
    • OkHttpClient 客户端 = new OkHttpClient.Builder().cache(cache).networkInterceptor(new CachingControlInterceptor()).build();
    • @Amr Barakat 我也想为 Post 方法实现缓存,可以吗?怎么样?
    【解决方案3】:
        OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
    

    【讨论】:

    • 请为您的解决方案提供一些信息或解释。
    【解决方案4】:

    你也可以试试:

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

    【讨论】:

      【解决方案5】:

      我终于在 Retrofit 2.x 和 OkHttp 3.x 中找到了适合我的解决方案

      我必须实现两个拦截器,其中一个负责重写请求标头,另一个负责重写响应标头

      1. 首先,确保删除所有旧缓存。 (根资源管理器 /data/data/com.yourapp/cache

      2. 实例化客户端生成器:

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

      务必确保将 ResponseCacheControlInterceptor 添加为网络拦截器,并将 RewriteRequestInterceptor 添加为拦截器(就像我在第二步中所做的那样)。

      【讨论】:

        猜你喜欢
        • 2016-08-11
        • 2015-12-13
        • 1970-01-01
        • 2016-09-23
        • 1970-01-01
        • 2012-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多