Java 脱机时是否可以使用OKHttp使用缓存数据进行改装

Java 脱机时是否可以使用OKHttp使用缓存数据进行改装,java,caching,retrofit,okhttp,offline-caching,Java,Caching,Retrofit,Okhttp,Offline Caching,我正在尝试使用改装&OKHttp来缓存HTTP响应。我遵循并最终得到了以下代码: File httpCacheDirectory=新文件(context.getCacheDir(),“responses”); HttpResponseCache HttpResponseCache=null; 试一试{ httpResponseCache=新的httpResponseCache(httpCacheDirectory,10*1024*1024); }捕获(IOE异常){ Log.e(“改装”,“无法

我正在尝试使用改装&OKHttp来缓存HTTP响应。我遵循并最终得到了以下代码:

File httpCacheDirectory=新文件(context.getCacheDir(),“responses”);
HttpResponseCache HttpResponseCache=null;
试一试{
httpResponseCache=新的httpResponseCache(httpCacheDirectory,10*1024*1024);
}捕获(IOE异常){
Log.e(“改装”,“无法创建http缓存”,e);
}
OkHttpClient OkHttpClient=新的OkHttpClient();
okHttpClient.setResponseCache(httpResponseCache);
api=new RestAdapter.Builder()
.setEndpoint(API_URL)
.setLogLevel(RestAdapter.LogLevel.FULL)
.setClient(新的OkClient(okHttpClient))
.build()
.create(MyApi.class);
这是带有缓存控制头的MyApi

公共接口MyApi{
@标头(“缓存控制:公共,最大使用年限=640000,s-maxage=640000,最大使用年限=2419200”)
@获取(“/api/v1/person/1/”)
无效请求人(
回调
);
首先,我联机请求并检查缓存文件。正确的JSON响应和头都在那里。但是当我尝试脱机请求时,我总是得到
RefughtError UnknownHostException
。我还应该做些什么来让Refugt从缓存读取响应

编辑: 由于OKHttp 2.0.x
HttpResponseCache
Cache
setResponseCache
setCache
为改装2.x编辑: OkHttp拦截器是脱机访问缓存的正确方法:

1) 创建拦截器:

private static final Interceptor REWRITE\u CACHE\u CONTROL\u Interceptor=new Interceptor(){
@覆盖公共响应拦截(链)引发IOException{
Response originalResponse=chain.procedure(chain.request());
if(Utils.isNetworkAvailable(上下文)){
int maxAge=60;//从缓存读取1分钟
返回originalResponse.newBuilder()
.header(“缓存控制”、“公共、最大年龄=“+maxAge”)
.build();
}否则{
int maxStale=60*60*24*28;//容忍4周的过期
返回originalResponse.newBuilder()
.header(“缓存控制”、“公共,仅当缓存时,max stale=“+maxStale”)
.build();
}
}
2) 安装客户端:

OkHttpClient=new-OkHttpClient();
client.networkInterceptors().add(重写\u缓存\u控制\u拦截器);
//设置缓存
File httpCacheDirectory=新文件(context.getCacheDir(),“responses”);
int cacheSize=10*1024*1024;//10 MiB
Cache Cache=新缓存(httpCacheDirectory,cacheSize);
//将缓存添加到客户端
setCache(cache);
3) 将客户端添加到改造中

reformation-reformation=new-reformation.Builder()
.baseUrl(基本URL)
.客户(客户)
.addConverterFactory(GsonConverterFactory.create())
.build();
同时检查@kosiara-Bartosz Kosarzycki。您可能需要从响应中删除一些标题


OKHttp 2.0.x(检查原始答案): 由于OKHttp 2.0.x
HttpResponseCache
Cache
setResponseCache
setCache
。因此您应该像这样
setCache

File httpCacheDirectory=新文件(context.getCacheDir(),“responses”);
Cache=null;
试一试{
cache=新缓存(httpCacheDirectory,10*1024*1024);
}捕获(IOE异常){
Log.e(“OKHttp”,“无法创建http缓存”,e);
}
OkHttpClient OkHttpClient=新的OkHttpClient();
if(缓存!=null){
okHttpClient.setCache(缓存);
}
String hostURL=context.getString(R.String.host\u url);
api=new RestAdapter.Builder()
.setEndpoint(主机URL)
.setClient(新的OkClient(okHttpClient))
.setRequestInterceptor(/*此处回答的其余部分*/)
.build()
.create(MyApi.class);

原始答案: 事实证明,服务器响应必须具有
缓存控制:public
才能使
OkClient
从缓存中读取

另外,如果您希望在可用时从网络请求,则应添加
缓存控制:max age=0
请求标头。显示了如何将其参数化。我就是这样使用它的:

RestAdapter.Builder=new RestAdapter.Builder()
.setRequestInterceptor(新的RequestInterceptor(){
@凌驾
公共无效截获(请求门面请求){
addHeader(“接受”、“应用程序/json;版本=1”);
if(MyApplicationUtils.isNetworkAvailable(上下文)){
int maxAge=60;//从缓存读取1分钟
request.addHeader(“缓存控制”、“公共、最大年龄=“+maxAge”);
}否则{
int maxStale=60*60*24*28;//容忍4周的过期
request.addHeader(“缓存控制”,
“public,仅当缓存时,max stale=“+maxStale”);
}
}
});

以上所有ANWSER对我都不起作用。我试图在改型2.0.0-beta2中实现脱机缓存。我使用
okHttpClient.networkInterceptors()添加了一个拦截器
方法,但在我尝试脱机使用缓存时收到了
java.net.UnknownHostException
。结果证明,我还必须添加
okHttpClient.interceptors()

问题是缓存未写入闪存,因为服务器返回
Pragma:no cache
,这会阻止OkHttp存储响应。脱机缓存即使在修改请求头值后也无法工作。经过一些尝试和错误后,我通过删除pr使缓存工作,而不修改后端
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
   );
OkHttpClient okHttpClient = createCachedClient(context);
Retrofit retrofit = new Retrofit.Builder()
        .client(okHttpClient)
        .baseUrl(API_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
service = retrofit.create(RestDataResource.class);
private OkHttpClient createCachedClient(final Context context) {
    File httpCacheDirectory = new File(context.getCacheDir(), "cache_file");

    Cache cache = new Cache(httpCacheDirectory, 20 * 1024 * 1024);
    OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.setCache(cache);
    okHttpClient.interceptors().add(
            new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request originalRequest = chain.request();
                    String cacheHeaderValue = isOnline(context) 
                        ? "public, max-age=2419200" 
                        : "public, only-if-cached, max-stale=2419200" ;
                    Request request = originalRequest.newBuilder().build();
                    Response response = chain.proceed(request);
                    return response.newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        .header("Cache-Control", cacheHeaderValue)
                        .build();
                }
            }
    );
    okHttpClient.networkInterceptors().add(
            new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request originalRequest = chain.request();
                    String cacheHeaderValue = isOnline(context) 
                        ? "public, max-age=2419200" 
                        : "public, only-if-cached, max-stale=2419200" ;
                    Request request = originalRequest.newBuilder().build();
                    Response response = chain.proceed(request);
                    return response.newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        .header("Cache-Control", cacheHeaderValue)
                        .build();
                }
            }
    );
    return okHttpClient;
}
public interface RestDataResource {

    @GET("rest-data") 
    Call<List<RestItem>> getRestData();

}
private BackendService() {

    httpCacheDirectory = new File(context.getCacheDir(),  "responses");
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache cache = new Cache(httpCacheDirectory, cacheSize);

    httpClient = new OkHttpClient.Builder()
            .addNetworkInterceptor(REWRITE_RESPONSE_INTERCEPTOR)
            .addInterceptor(OFFLINE_INTERCEPTOR)
            .cache(cache)
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.backend.com")
            .client(httpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    backendApi = retrofit.create(BackendApi.class);
}

private static final Interceptor REWRITE_RESPONSE_INTERCEPTOR = chain -> {
    Response originalResponse = chain.proceed(chain.request());
    String cacheControl = originalResponse.header("Cache-Control");

    if (cacheControl == null || cacheControl.contains("no-store") || cacheControl.contains("no-cache") ||
            cacheControl.contains("must-revalidate") || cacheControl.contains("max-age=0")) {
        return originalResponse.newBuilder()
                .header("Cache-Control", "public, max-age=" + 10)
                .build();
    } else {
        return originalResponse;
    }
};

private static final Interceptor OFFLINE_INTERCEPTOR = chain -> {
    Request request = chain.request();

    if (!isOnline()) {
        Log.d(TAG, "rewriting request");

        int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
        request = request.newBuilder()
                .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                .build();
    }

    return chain.proceed(request);
};

public static boolean isOnline() {
    ConnectivityManager cm = (ConnectivityManager) MyApplication.getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = cm.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}
OkHttpClient client = new OkHttpClient
  .Builder()
  .cache(new Cache(App.sApp.getCacheDir(), 10 * 1024 * 1024)) // 10 MB
  .addInterceptor(new Interceptor() {
    @Override public Response intercept(Chain chain) throws IOException {
      Request request = chain.request();
      if (NetworkUtils.isNetworkAvailable()) {
        request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build();
      } else {
        request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build();
      }
      return chain.proceed(request);
    }
  })
  .build();
public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager cm =
                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        return activeNetwork != null &&
                activeNetwork.isConnectedOrConnecting();
    }
Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .client(client)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
@Test
public void test_USE_CACHE_WHEN_OFFLINE() throws Exception {
    //given
    givenResponseInCache("Expired Response in cache", -5, MINUTES);
    given(networkMonitor.isOnline()).willReturn(false);

    //when
    //This response is only used to not block when test fails
    mockWebServer.enqueue(new MockResponse().setResponseCode(404));
    Response response = getResponse();

    //then
    then(response.body().string()).isEqualTo("Expired Response in cache");
    then(cache.hitCount()).isEqualTo(1);
}