2016-07-24 58 views
2

从这个问题给出的答案Can Retrofit with OKHttp use cache data when offline我能够想出这个,但代码似乎不缓存。我可能做错了什么?Retrofit 2&OKHttp 3缓存响应不工作在我的情况

这是我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); 

回答

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

您不应该重写来自服务器的响应来促进缓存。最好让服务器的管理员在提供响应时包含缓存标头。这种方式缓存适用于所有客户端 - 不只是OkHttp。

也就是说,你是一个成年人,你有权采取技术捷径,避免与你的服务器团队交谈。

首先,您需要更改代码以同时使用network interceptor和应用程序拦截器。为什么两个?那么,当你向缓存发送请求时,网络拦截器还没有运行。当你将响应写入缓存时,应用程序拦截器还没有运行。

您的应用程序拦截器会重写你的请求头,当你喜欢缓存,包括这一点,并允许缓存服务过期响应:

Cache-Control: only-if-cached, max-stale=86400 

您的网络拦截器会重写你的服务器的响应头包含此以便所有响应缓存24小时:

Cache-Control: max-age=86400 
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; 
}