草庐IT

android - OkHttp3 缓存似乎未通过 Retrofit 2 检查

coder 2023-12-16 原文

我正在尝试使用 Retrofit (2.1.0) 和 OkHttp (3.3.1) 设置 HTTP 缓存。我看过很多与此主题相关的帖子,但都没有帮助。

我写了一些单元测试来查看缓存是如何工作的。它工作得很好,但一旦集成到我的应用程序中,魔法就结束了。我将首先向您展示我的实现,然后解释我的一些调查。

首先,这是我的 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();

这里是拦截器添加一个头来设置缓存控制:

    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;
        }
    };

最后一个拦截器添加了 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);
        }
    };

最后是我服务的单一方法:

 @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);

关于我的调查,我设置了一个拦截器记录器(应用端,而不是网络)来查看发生了什么。我可以在日志中看到诸如“Cache-Control: public, max-stale=500”之类的行。这意味着(至少对我而言) header 应该为 OkHttp 客户端提供检查缓存的机会。

缓存本身似乎已正确初始化。当我创建它时,我强制初始化并记录缓存中存在的所有 url。下面是它的实现方式:

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");
        }

当我在 Wifi 可用的情况下启动我的应用程序时,我得到了预期的响应。然后我终止我的应用程序,禁用 Wifi 并重新启动该应用程序。我希望缓存此时可以提供数据。但它失败了,我只能在日志中看到 OkHttp 打印的行:

HTTP FAILED: java.net.UnknownHostException: Unable to resolve host "my-domain.com": No address associated with hostname

最后一件事,在 RFC 2616 中,可以阅读:

max-stale : Indicates that the client is willing to accept a response that has exceeded its expiration time. 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. If no value is assigned to max-stale, then the client is willing to accept a stale response of any age.

当我没有指定一个值时,它实际上可以工作(即使 Wifi 关闭我也会收到响应)。现在这是我发现让它“工作”的唯一方法。所以也许我只是误解了缓存控制指令!?

此时我真的很困惑。我真的很想能够使用 OkHttp 缓存系统,但不知何故我遗漏了一些东西。

感谢您阅读所有文字!

最佳答案

使用该方法创建缓存的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;


        }

调用createCachedClient()方法创建OkHttpClient 添加此客户端进行改造

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

将此权限添加到 list

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

如果互联网第一次可用,它将调用该服务并缓存请求,下一次最多 2419200 毫秒,它将使用缓存提供响应。即使设备离线,它也不会在 2419200 毫秒内访问服务器。

关于android - OkHttp3 缓存似乎未通过 Retrofit 2 检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40532912/

有关android - OkHttp3 缓存似乎未通过 Retrofit 2 检查的更多相关文章

  1. ruby - 如何在 Ubuntu 中清除 Ruby Phusion Passenger 的缓存? - 2

    我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:

  2. ruby-on-rails - Ruby on Rails 计数器缓存错误 - 2

    尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot

  3. ruby-on-rails - 在 heroku 的 .fonts 文件夹中包含自定义字体,似乎无法识别它们 - 2

    Heroku支持人员告诉我,为了在我的Web应用程序中使用自定义字体(未安装在系统中,您可以在bash控制台中使用fc-list查看已安装的字体)我必须部署一个包含所有字体的.fonts文件夹里面的字体。问题是我不知道该怎么做。我的意思是,我不知道文件名是否必须遵循heroku的任何特殊模式,或者我必须在我的代码中做一些事情来考虑这种字体,或者如果我将它包含在文件夹中它是自动的......事实是,我尝试以不同的方式更改字体的文件名,但根本没有使用该字体。为了提供更多详细信息,我们使用字体的过程是将PDF转换为图像,更具体地说,使用rghostgem。并且最终图像根本不使用自定义字体。在

  4. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  5. ruby-on-rails - "rails generate rspec:install"似乎失败了 - 2

    运行:ruby1.9.3p0和Rails3.2.1尝试使用rspec但当我尝试将其安装到我的应用程序中时出现以下错误:/Users/Si/.rvm/gems/ruby-1.9.3-p0/gems/railties-3.2.1/lib/rails/railtie/configuration.rb:85:in`method_missing':undefinedmethod`generators'for#(NoMethodError)from/Users/Si/.rvm/gems/ruby-1.9.3-p0/gems/rspec-rails-2.0.0.beta.18/lib/rspec-r

  6. ruby-on-rails - bundle 安装尝试使用缓存文件 - 2

    当我尝试进行bundle安装时,我的gem_path和gem_home指向/usr/local/rvm/gems/我没有写入权限,并且由于权限无效而失败。因此,我已将两个路径都更改为我具有写入权限的本地目录。这样做时,我进行了bundle安装,我得到:bruno@test6:~$bundleinstallFetchinggemmetadatafromhttps://rubygems.org/.........Fetchinggemmetadatafromhttps://rubygems.org/..Bundler::GemspecError:Couldnotreadgemat/afs/

  7. ruby-on-rails - Heroku Action 缓存似乎不起作用 - 2

    我一直在Heroku上尝试不同的缓存策略,并添加了他们的memcached附加组件,目的是为我的应用程序添加Action缓存。但是,当我在我当前的应用程序上查看Rails.cache.stats时(安装了memcached并使用dalligem),在执行应该缓存的操作后,我得到current和total_items为0。在Controller的顶部,我想缓存我有的Action:caches_action:show此外,我修改了我的环境配置(对于在Heroku上运行的配置)config.cache_store=:dalli_store我是否可以查看其他一些统计数据,看看它是否有效或我做错

  8. ruby-on-rails - rails expire_page 没有删除缓存的文件 - 2

    我有一个具有页面缓存的ControllerAction,我制作了一个清扫程序,它使用Controller和指定的Action调用expire_page...Controller操作呈现一个js.erb模板,所以我试图确保expire_page删除public/javascripts中的.js文件,但它没有这样做。classJavascriptsController"javascripts",:action=>"lol",:format=>'js')endend...所以,我访问javascripts/lol.js并呈现我的模板。我验证了public/javascripts/lol.js

  9. ruby-on-rails - rails 3 缓存 : expire action for named route - 2

    我的Controller有这个:caches_action:render_ticker_for_channel,:expires_in=>30.seconds在我的路由文件中我有这个:match'/render_c_t/:channel_id'=>'render#render_ticker_for_channel',:as=>:render_channel_ticker在日志文件中我看到了这个:Writefragmentviews/mcr3.dev/render_c_t/63(11.6ms)我如何手动使它过期?我需要从与渲染Controller不同的Controller使它过期,但即使

  10. ruby - 更新 gem 时 Docker 包安装缓存问题 - 2

    我在开发和生产中都使用docker,真正困扰我的一件事是docker缓存的简单性。我的ruby​​应用程序需要bundleinstall来安装依赖项,因此我从以下Dockerfile开始:添加GemfileGemfile添加Gemfile.lockGemfile.lock运行bundleinstall--path/root/bundle所有依赖项都被缓存,并且在我添加新gem之前效果很好。即使我添加的gem只有0.5MB,从头开始安装所有应用程序gem仍然需要10-15分钟。由于依赖项文件夹的大小(大约300MB),然后再花10分钟来部署它。我在node_modules和npm上遇到了

随机推荐