离线时可以使用OKHttp改造使用缓存数据

新手上路,请多包涵

我正在尝试使用 Retrofit 和 OKHttp 来缓存 HTTP 响应。我遵循 了这个要点,最后得到了这段代码:

 File httpCacheDirectory = new File(context.getCacheDir(), "responses");

HttpResponseCache httpResponseCache = null;
try {
     httpResponseCache = new HttpResponseCache(httpCacheDirectory, 10 * 1024 * 1024);
} catch (IOException e) {
     Log.e("Retrofit", "Could not create http cache", e);
}

OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setResponseCache(httpResponseCache);

api = new RestAdapter.Builder()
          .setEndpoint(API_URL)
          .setLogLevel(RestAdapter.LogLevel.FULL)
          .setClient(new OkClient(okHttpClient))
          .build()
          .create(MyApi.class);

这是带有 Cache-Control 标头的 MyApi

 public interface MyApi {
   @Headers("Cache-Control: public, max-age=640000, s-maxage=640000 , max-stale=2419200")
   @GET("/api/v1/person/1/")
   void requestPerson(
           Callback<Person> callback
   );

首先我在线请求并检查缓存文件。那里有正确的 JSON 响应和标头。但是当我尝试离线请求时,我总是得到 RetrofitError UnknownHostException 。我还应该做些什么来让 Retrofit 从缓存中读取响应吗?

编辑: 既然OKHTTP 2.0.x HttpResponseCache Cache setResponseCache setCache

原文由 osrl 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 406
2 个回答

为 Retrofit 2.x 编辑:

OkHttp 拦截器是离线访问缓存的正确方式:

1)创建拦截器:

 private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
    @Override public Response intercept(Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());
        if (Utils.isNetworkAvailable(context)) {
            int maxAge = 60; // read from cache for 1 minute
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, max-age=" + maxAge)
                    .build();
        } else {
            int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .build();
        }
    }

2)设置客户端:

 OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR);

//setup cache
File httpCacheDirectory = new File(context.getCacheDir(), "responses");
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(httpCacheDirectory, cacheSize);

//add cache to the client
client.setCache(cache);

3)添加客户端进行改造

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

还要检查 @kosiara-Bartosz Kosarzycki回答。您可能需要从响应中删除一些标头。


OKHttp 2.0.x(查看原答案):

因为OKHTTP 2.0.X HttpResponseCache Cache setResponseCache setCache 所以你应该 setCache 像这样:

         File httpCacheDirectory = new File(context.getCacheDir(), "responses");

        Cache cache = null;
        try {
            cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
        } catch (IOException e) {
            Log.e("OKHttp", "Could not create http cache", e);
        }

        OkHttpClient okHttpClient = new OkHttpClient();
        if (cache != null) {
            okHttpClient.setCache(cache);
        }
        String hostURL = context.getString(R.string.host_url);

        api = new RestAdapter.Builder()
                .setEndpoint(hostURL)
                .setClient(new OkClient(okHttpClient))
                .setRequestInterceptor(/*rest of the answer here */)
                .build()
                .create(MyApi.class);


原答案:

事实证明,服务器响应必须有 Cache-Control: public 才能使 OkClient 从缓存中读取。

此外,如果您想在可用时从网络请求,则应添加 Cache-Control: max-age=0 请求标头。 This answer 显示了如何对其进行参数化。这就是我使用它的方式:

 RestAdapter.Builder builder= new RestAdapter.Builder()
   .setRequestInterceptor(new RequestInterceptor() {
        @Override
        public void intercept(RequestFacade request) {
            request.addHeader("Accept", "application/json;versions=1");
            if (MyApplicationUtils.isNetworkAvailable(context)) {
                int maxAge = 60; // read from cache for 1 minute
                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);
            }
        }
});

原文由 osrl 发布,翻译遵循 CC BY-SA 3.0 许可协议

上面的所有答案都不适合我。我尝试在 retrofit 2.0.0-beta2 中实现离线缓存。我使用 okHttpClient.networkInterceptors() 方法添加了一个拦截器,但是当我尝试离线使用缓存时收到 java.net.UnknownHostException 。事实证明,我还必须添加 okHttpClient.interceptors()

问题是缓存没有写入闪存,因为服务器返回了 Pragma:no-cache 这阻止了 OkHttp 存储响应。即使在修改请求标头值后,离线缓存也不起作用。经过一些反复试验后,我通过从响应而不是请求中删除 pragma 来让缓存工作而无需修改后端 - response.newBuilder().removeHeader("Pragma");

改造: 2.0.0-beta2 ; OkHttp: 2.5.0

 OkHttpClient okHttpClient = createCachedClient(context);
Retrofit retrofit = new Retrofit.Builder()
        .client(okHttpClient)
        .baseUrl(API_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
service = retrofit.create(RestDataResource.class);

 private OkHttpClient createCachedClient(final Context context) {
    File httpCacheDirectory = new File(context.getCacheDir(), "cache_file");

    Cache cache = new Cache(httpCacheDirectory, 20 * 1024 * 1024);
    OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.setCache(cache);
    okHttpClient.interceptors().add(
            new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request originalRequest = chain.request();
                    String cacheHeaderValue = isOnline(context)
                        ? "public, max-age=2419200"
                        : "public, only-if-cached, max-stale=2419200" ;
                    Request request = originalRequest.newBuilder().build();
                    Response response = chain.proceed(request);
                    return response.newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        .header("Cache-Control", cacheHeaderValue)
                        .build();
                }
            }
    );
    okHttpClient.networkInterceptors().add(
            new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request originalRequest = chain.request();
                    String cacheHeaderValue = isOnline(context)
                        ? "public, max-age=2419200"
                        : "public, only-if-cached, max-stale=2419200" ;
                    Request request = originalRequest.newBuilder().build();
                    Response response = chain.proceed(request);
                    return response.newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        .header("Cache-Control", cacheHeaderValue)
                        .build();
                }
            }
    );
    return okHttpClient;
}

 public interface RestDataResource {

    @GET("rest-data")
    Call<List<RestItem>> getRestData();

}

原文由 kosiara - Bartosz Kosarzycki 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题