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();
}
}
私のRetrofit
およびOkHttpClient
インスタンス:
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
最後に私は答えを得ます。
Network Interceptorは次のようにする必要があります。
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());
CachingControlInterceptor
では、新しいリクエストを作成しますが、実際には使用しません。 newBuilder
を呼び出して結果を無視するため、ヘッダーの変更は実際にはどこにも送信されません。それらの値をrequest
に割り当ててから、proceed
をchain.request()
で呼び出す代わりに、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();
}
}
あなたも試すことができます:
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);
}
}
ついにRetrofit 2.xとOkHttp 3.xで機能するソリューションを見つけました
2つのインターセプターを実装する必要があり、そのうちの1つはRequest headersを書き換え、もう1つはResponse headersを書き換える責任があります。
まず、古いキャッシュを削除してください。 (ルートエクスプローラー/data/data/com.yourapp/cache
クライアントビルダーをインスタンス化します。
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(new RewriteRequestInterceptor())
.addNetworkInterceptor(new RewriteResponseCacheControlInterceptor())
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);
}
}
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をインターセプターとして追加することを確認することが重要です(2番目のステップで行ったように)