从这个问题的答案Can Retrofit with OKHttp use cache data when offline我能够想出这个,但代码似乎没有缓存。我能做错什么?
这是我的okhttp客户端
long SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MB
Cache cache = new Cache(getDirectory(), SIZE_OF_CACHE);
if (cache == null) {
Toast.makeText(AppController.getInstance().getApplicationContext(), "could n0t set cache", Toast.LENGTH_SHORT).show();
}
client = new OkHttpClient
.Builder()
.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
.cache(cache)
.build();
添加我的网络拦截器如下:
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 (isConnected()) {
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; // tolerate 1-day stale
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
}
};
我正在加入这样的改造:
public static Retrofit getClient() {
createCacheForOkHTTP();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
//添加我的活动:
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<MovieResponse> call = apiService.getPopularMoviesDetails(ApiKey, page);
call.enqueue(new Callback<MovieResponse>() {
@Override
public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
progressBar.setVisibility(View.GONE);
mErrorView.setVisibility(View.GONE);
if (response.isSuccessful()) {
movies = response.body().getResults();
movieAdapter.setMovieList(movies);
mRecyclerView.setAdapter(movieAdapter);
} else {
Toast.makeText(getActivity(), "header" + response.headers() + "code" + response.code() + "errorbody" + response.errorBody() + "errorbody" + response.message(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<MovieResponse> call, Throwable t) {
progressBar.setVisibility(View.GONE);
// Log error here since request failed
Log.e(TAG, t.toString());
mErrorView.setVisibility(View.VISIBLE);
}
});
//接口
@GET("movie/popular")
Call<MovieResponse> getPopularMoviesDetails(@Query("api_key") String apiKey, @Query("page") int page);
答案 0 :(得分:2)
您不应该重写服务器的响应以方便缓存。最好让服务器管理员在提供响应时包含缓存标头。这样,缓存适用于所有客户端 - 而不仅仅是OkHttp。
那就是说,你是一个成年人,你有权采取技术捷径,以避免与你的服务器团队交谈。
首先,您需要更改代码以同时使用network interceptor和应用程序拦截器。为什么两个?好吧,当你向缓存发出请求时,网络拦截器还没有运行。当您将响应写入缓存时,应用程序拦截器尚未运行。
您的应用程序拦截器将重写您的请求标头,以便在您更喜欢缓存时包含此标头,并允许缓存提供过时的响应:
GET http://localhost:54/i18n/en.json 404 (Not Found)
您的网络拦截器将重写服务器的响应标头以包含此标题,以便所有响应都缓存24小时:
Cache-Control: only-if-cached, max-stale=86400
答案 1 :(得分:1)
不确定您是否已经解决了这个问题。我找到了一个很好/简短的解决方案,希望它有所帮助。
我创建了一个 CacheControlInterceptor 。这有24小时的缓存,如果您处于脱机状态且响应时间不到24小时,您将获得缓存响应。
public class CacheControlInterceptor implements Interceptor {
private static final String CACHE_CONTROL_HEADER = "Cache-Control";
private static final String MAX_AGE_HEADER_VALUE = "public, max-age=";
private static final String MAX_STALE_HEADER_VALUE = "public, only-if-cached, max-stale=";
// Specifies the maximum amount of time a resource will be considered fresh.
private static final int MAX_AGE = 60;
// Indicates that the client is willing to accept a response that has exceeded its expiration time.
private static final int MAX_STALE = 60 * 60 * 24; // 24 hours cache.
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (WatchItApplication.hasNetwork()) {
request = request.newBuilder()
.header(CACHE_CONTROL_HEADER, MAX_AGE_HEADER_VALUE + MAX_AGE).build();
} else {
request = request.newBuilder()
.header(CACHE_CONTROL_HEADER, MAX_STALE_HEADER_VALUE + MAX_STALE).build();
}
return chain.proceed(request);
}
}
在这个方法中,我创建 OKHttpCliente 。正如您所注意到的,我正在添加CacheControlInterceptor和缓存。
private OkHttpClient createDefaultOkHttpClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
return new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.connectTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
.readTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
.writeTimeout(TIMEOUT_MILLIS, TIMEOUT_UNIT)
.addNetworkInterceptor(new HeaderInterceptor())
.cache(new Cache(WatchItApplication.getInstance().getCacheDir(), 10 * 1024 * 1024))
.addInterceptor(new CacheControlInterceptor())
.addInterceptor(interceptor)
.build();
}
最后改造:
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(createDefaultOkHttpClient())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
如果有效,请告诉我。
答案 2 :(得分:0)
我能够解决这个问题。这就是我做到的。
private static OkHttpClient getCacheClient(final Context context) {
Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response originalResponse = chain.proceed(chain.request());
if (isConnected()) {
// Internet available; read from cache for 0 day
// Why? Reduce server load, better UX
int maxAge = 0;
return originalResponse.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
// No internet; tolerate cached data for 1 week
int maxStale = 60 * 60 * 24 * 7;
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
}
};
File httpCacheDirectory = new File(context.getCacheDir(), "cachedir");
int size = 5 * 1024 * 1024;
Cache cache = new Cache(httpCacheDirectory, size);
return new OkHttpClient.Builder()
.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
.cache(cache)
.build();
}
用法:
public static Retrofit getClient(final Context context) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.client(getCacheClient(context))
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}