1. ホーム
  2. java

[解決済み] OKHttpを搭載したRetrofitはオフライン時にキャッシュデータを使用できますか?

2022-04-26 01:41:56

質問

Retrofit & OKHttp を使って HTTP レスポンスをキャッシュしようとしています。私は以下のようにしました。 このgist で、最終的にこのようなコードになりました。

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にキャッシュからレスポンスを読ませるために、他に何かすべきことはありますか?

EDITです。 OKHttp 2.0.x以降 HttpResponseCacheCache , setResponseCachesetCache

解決方法は?

Retrofit 2.x用に編集してください。

OkHttp Interceptorは、オフラインの時にキャッシュにアクセスするための正しい方法です。

1) Interceptorを作成します。

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

以下もご確認ください。 コシアラ(Bartosz Kosarzycki)さん 's 回答 . レスポンスからいくつかのヘッダーを削除する必要があるかもしれません。


OKHttp 2.0.x (元の答えを確認する)。

OKHttp 2.0.x以降 HttpResponseCacheCache , setResponseCachesetCache . だから、あなたは 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 リクエストヘッダを表示します。 この回答 は、パラメータ化された方法を示しています。私はこのように使いました。

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