简体   繁体   English

OkHttp3缓存似乎未使用Retrofit 2进行检查

[英]OkHttp3 cache seems to be unchecked with Retrofit 2

I'm trying to setup an HTTP cache using Retrofit (2.1.0) and OkHttp (3.3.1). 我正在尝试使用Retrofit(2.1.0)和OkHttp(3.3.1)设置HTTP缓存。 I have seen many posts related to this topic, but none of them helped. 我看过很多与此主题相关的帖子,但都没有帮助。

I wrote some unit tests to see how the cache works. 我写了一些单元测试来查看缓存是如何工作的。 It works just fine, but once integrated in my app, the magic ends. 它工作正常,但一旦集成在我的应用程序中,魔术就结束了。 I will first show you my implementation and then explain some of my investigation. 我将首先向您展示我的实施,然后解释我的一些调查。

First, here is my Retrofit instantiation : 首先,这是我的Retrofit实例化:

    OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);

    OkHttpClient client = httpBuilder
            .addNetworkInterceptor(INTERCEPTOR_RESPONSE_SET_CACHE)
            .addNetworkInterceptor(INTERCEPTOR_REQUEST_ADD_CHECKSUM)
            .addInterceptor(loggingInterceptor)
            .cache(cacheHttpClient).build();

    Retrofit retrofit = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .baseUrl(BASE_URL)
            .build();

Here is the interceptor adding a header to set cache control: 这是拦截器添加标头来设置缓存控件:

    private final Interceptor INTERCEPTOR_RESPONSE_SET_CACHE = new Interceptor() {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Response response = chain.proceed(chain.request());
            response = response.newBuilder()
                    .header("Cache-Control", "max-age=600") //+ Integer.toString(3600 * 5)
                    .build();
            return response;
        }
    };

The last interceptor adds 2 URL parameters: 最后一个拦截器添加了2个URL参数:

 private static final Interceptor INTERCEPTOR_REQUEST_ADD_CHECKSUM = new Interceptor() {
        @Override
        public Response intercept(Interceptor.Chain chain) throws IOException {
            HttpUrl url = chain.request().url();
            url = url.newBuilder().addQueryParameter("rd", "random1").addQueryParameter("chk","check1").build();
            Request request = chain.request().newBuilder().url(url).build();
            return chain.proceed(request);
        }
    };

Finally, the single method of my service : 最后,我的服务的单一方法:

 @Headers("Cache-Control: public, max-stale=500")
 @GET("/get_data")
 Call<DataResponse> getData(@Query("year") int year, @Query("month") int month, @Query("day") int day);

About my investigation, I setup an interceptor logger (app side, not network) to see what is happening. 关于我的调查,我设置了一个拦截器记录器(应用程序端,而不是网络)来查看发生了什么。 I can see lines such as "Cache-Control: public, max-stale=500" in my logs. 我可以在日志中看到诸如“Cache-Control:public,max-stale = 500”之类的行。 This means (at least to me) that the header should give an opportunity to the OkHttp client to check the cache. 这意味着(至少对我而言)标题应该为OkHttp客户端提供检查缓存的机会。

The cache itself seems to be correctly initialised. 缓存本身似乎已正确初始化。 When I create it, I force the initialisation and log all the urls present in the cache. 当我创建它时,我强制初始化并记录缓存中存在的所有URL。 Here is how it is implemented: 以下是它的实现方式:

File httpCacheDirectory = new File(getCacheDir(), "responses");
        httpCacheDirectory.getParentFile().mkdirs();
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        Cache cache = new Cache(httpCacheDirectory, cacheSize);
        try {
            cache.initialize();
            Iterator<String> iterator = cache.urls();
            Log.i(TAG, "URLs in cacheHttpClient : ");
            while (iterator.hasNext()) {
                Log.i(TAG, iterator.next());
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.i(TAG, "CACHE NOT INIT");
        }

When I launch my app with Wifi available, I get the expected responses. 当我使用Wifi启动我的应用程序时,我得到了预期的响应。 Then I kill my app, disable Wifi and relaunch the app. 然后我杀了我的应用程序,禁用Wifi并重新启动应用程序。 I expect the cache to serve data at this moment. 我希望此时缓存能够提供数据。 But it fails and I can only see OkHttp printed lines in logs : 但它失败了,我只能在日志中看到OkHttp打印的行:

HTTP FAILED: java.net.UnknownHostException: Unable to resolve host "my-domain.com": No address associated with hostname HTTP FAILED:java.net.UnknownHostException:无法解析主机“my-domain.com”:没有与主机名关联的地址

Last thing, in RFC 2616 , one can read : 最后,在RFC 2616中 ,可以阅读:

max-stale : Indicates that the client is willing to accept a response that has exceeded its expiration time. max-stale:表示客户端愿意接受超过其到期时间的响应。 If max-stale is assigned a value, then the client is willing to accept a response that has exceeded its expiration time by no more than the specified number of seconds. 如果为max-stale分配了值,则客户端愿意接受超过其到期时间的响应不超过指定的秒数。 If no value is assigned to max-stale, then the client is willing to accept a stale response of any age. 如果没有为max-stale分配值,则客户愿意接受任何年龄的陈旧响应。

When I don't specify an value, it actually works (I get a response even when the Wifi is down). 当我没有指定一个值时,它实际上是有效的(即使在Wifi关闭时我也会得到响应)。 For now this is the only way I found to make it "work". 现在,这是我发现让它“工作”的唯一方法。 So maybe I just misunderstand the cache-control directive !? 所以也许我只是误解了缓存控制指令!

At this point I'm really confused. 此时我真的很困惑。 I really would like to be able to use OkHttp cache system, but somehow I'm missing something. 我真的希望能够使用OkHttp缓存系统,但不知怎的,我错过了一些东西。

Thank you for reading all that text ! 感谢您阅读所有文字!

Use this method to create cached okkhttpclient 使用此方法创建缓存的okkhttpclient

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 com.squareup.okhttp.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();
                        com.squareup.okhttp.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 com.squareup.okhttp.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();
                        com.squareup.okhttp.Response response = chain.proceed(request);
                        return response.newBuilder()
                                .removeHeader("Pragma")
                                .removeHeader("Cache-Control")
                                .header("Cache-Control", cacheHeaderValue)
                                .build();
                    }
                }
        );
        return okHttpClient;
    }

    private boolean isOnline(Context context) {
        ConnectivityManager connectivity = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivity != null) {
            NetworkInfo[] info = connectivity.getAllNetworkInfo();
            if (info != null)
                for (int i = 0; i < info.length; i++)
                    if (info[i].getState() == NetworkInfo.State.CONNECTED) {
                        return true;
                    }
        }
        return false;


        }

Call createCachedClient() method to create OkHttpClient add this client to retrofit 调用createCachedClient()方法创建OkHttpClient添加此客户端进行改造

OkHttpClient okHttpClient = createCachedClient(MainActivity.this);
Retrofit retrofit=new Retrofit.Builder()
         .client(okHttpClient)
         .baseUrl(API)
         .addConverterFactory(GsonConverterFactory
         .create()).build();

Add this permission to manifest 将此权限添加到清单

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

If internet is available first time it will call the service and cache the request,next time onwards upto 2419200 milliseconds it will use cache to give response.it won't hit server upto 2419200 milliseconds even if device if offline. 如果互联网第一次可用,它将调用服务并缓存请求,下次最多2419200毫秒,它将使用缓存来响应。即使设备离线,也不会达到2419200毫秒。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM