2016-07-24 59 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; 
}