改造 OKHTTP 离线缓存不起作用

2023-12-19

我阅读了数十个教程和 Stackoverflow 来解决我的问题,但没有任何效果对我有用!而且,它们中的大多数都很旧,所以 OKHTTP 可能以某种方式发生了变化。

我想要的只是启用离线缓存用于改造。

我正在使用 GET

我尝试仅使用offlineCacheInterceptor作为拦截器,但我不断得到:

Unable to resolve host "jsonplaceholder.typicode.com": No address associated with hostname

我尝试结合使用offlineCacheInterceptor作为拦截器 +provideCacheInterceptor()作为一个 NetworkInterceptor,但我不断得到:

504 Unsatisfiable Request (only-if-cached) and a null response.body()

我什至确保添加.removeHeader("Pragma")到处!


我尝试了所有这些链接:

https://new Fivefour.com/android-retrofit2-okhttp3-cache-network-request-offline.html https://newfivefour.com/android-retrofit2-okhttp3-cache-network-request-offline.html(一个拦截器,不起作用!!)

https://medium.com/mindorks/caching-with-retrofit-store-responses-offline-71439ed32fda https://medium.com/mindorks/caching-with-retrofit-store-responses-offline-71439ed32fda(一个拦截器,不起作用!)

https://caster.io/lessons/retrofit-2-offline-cache https://caster.io/lessons/retrofit-2-offline-cache(单独的在线+离线缓存,不起作用)

https://www.journaldev.com/23297/android-retrofit-okhttp-offline-caching https://www.journaldev.com/23297/android-retrofit-okhttp-offline-caching(不工作,504 不可满足的请求(仅当缓存时))

http://mikescamell.com/gotcha-when-offline-caching-with-okhttp3/ http://mikescamell.com/gotcha-when-offline-caching-with-okhttp3/(一个拦截器,不起作用!!)

https://stackoverflow.com/a/48295397/8086424 https://stackoverflow.com/a/48295397/8086424(不工作) 无法解析主机“jsonplaceholder.typicode.com”:没有与主机名关联的地址

用 OKHttp 进行 Retrofit 离线时可以使用缓存数据吗 https://stackoverflow.com/questions/23429046/can-retrofit-with-okhttp-use-cache-data-when-offline(太混乱了!)


这是我的代码:

public static Retrofit getRetrofitInstance(Context context) {
        if (retrofit == null) {
            c = context;
            int cacheSize = 10 * 1024 * 1024; // 10 MB
            Cache cache = new Cache(context.getCacheDir(), cacheSize);
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .addInterceptor(provideHttpLoggingInterceptor())
                    .addInterceptor(offlineCacheInterceptor)
                    .addNetworkInterceptor(provideCacheInterceptor())
                    .cache(cache)
                    .build();
            //////////////////////////
            retrofit = new retrofit2.Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(okHttpClient)
                    .build();
        }
        return retrofit;
    }

 public static Interceptor offlineCacheInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Log.e("bbbb", "bbbb");
            if (!checkInternetAvailability()) {
                Log.e("aaaaa", "aaaaaa");
                CacheControl cacheControl = new CacheControl.Builder()
                        .maxStale(30, TimeUnit.DAYS)
                        .build();

                request = request.newBuilder()
                        .cacheControl(cacheControl)
                        .removeHeader("Pragma")
                        .build();
            }
            return chain.proceed(request);
        }
    };

 public static Interceptor provideCacheInterceptor() {
        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response response = chain.proceed(chain.request());

                // re-write response header to force use of cache
                CacheControl cacheControl = new CacheControl.Builder()
                        .maxAge(2, TimeUnit.MINUTES)
                        .build();

                return response.newBuilder()
                        .header(CACHE_CONTROL, cacheControl.toString())
                        .removeHeader("Pragma")
                        .build();
            }
        };
    }

我正在使用 jsonplaceholder.typicode.com/photos 返回:

content-type: application/json; charset=utf-8
    date: Sun, 21 Oct 2018 14:26:41 GMT
    set-cookie: __cfduid=d9e935012d2f789245b1e2599a41e47511540132001; expires=Mon, 21-Oct-19 14:26:41 GMT; path=/; domain=.typicode.com; HttpOnly
    x-powered-by: Express
    vary: Origin, Accept-Encoding
    access-control-allow-credentials: true
    expires: Sun, 21 Oct 2018 18:26:41 GMT
    x-content-type-options: nosniff
    etag: W/"105970-HCYFejK2YCxztz8++2rHnutkPOQ"
    via: 1.1 vegur
    cf-cache-status: REVALIDATED
    expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
    server: cloudflare
    cf-ray: 46d466910cab3d77-MXP
    Cache-Control: public, max-age=60

2021 年 6 月(Retrofit 2.9.0 或 OKHTTP 3.14.9)完整解决方案(更新)

自 2018 年 10 月以来,同样的方法仍然有效

2018 年 10 月(Retrofit 2.4 或 OKHTTP 3.11)完整解决方案

好的,使用 OKHTTP 或 Retrofit 进行在线和离线缓存已经给 stackoverflow 和其他论坛上的许多人带来了很多问题。互联网上有大量误导性信息和不起作用的代码示例。

因此,今天我将解释如何使用 Retrofit 和 OKHTTP 实现在线和离线缓存,并提供清晰的步骤 + 如何测试并了解您是否从缓存或网络获取数据。

如果您得到的是504 Unsatisfiable Request (only-if-cached) OR an Unable to resolve host "HOST": No address associated with hostname那么您可以使用以下任何解决方案。

在开始之前,您必须始终记住:

  • 确保您使用的是 GET 请求而不是 POST!
  • 始终确保添加.removeHeader("Pragma")如下所示(这可以让你覆盖服务器的缓存协议)
  • 测试时避免使用 HttpLoggingInterceptor,它可能会在开始时引起一些混乱。如果需要的话,最后启用它。
  • 如果您想探索使用拦截器,请始终从设备中删除您的应用程序,并在每次代码更改时重新安装它。否则,在旧的缓存数据仍在设备上时更改代码将会给您带来很多混乱和误导性的推论!
  • 向 OKHTTPClient 对象添加拦截器的顺序很重要!

注意:如果您想依赖服务器的缓存协议进行在线和离线缓存,那么请不要阅读这 2 个解决方案。只需阅读此内容article https://futurestud.io/tutorials/retrofit-2-activate-response-caching-etag-last-modified。您所需要做的就是创建一个缓存对象并将其附加到 HTTPClient 对象。


解决方案一:(更长,但你可以完全控制)

  • 步骤1:(创建在线拦截器)

        static Interceptor onlineInterceptor = new Interceptor() {
         @Override
         public okhttp3.Response intercept(Chain chain) throws IOException {
             okhttp3.Response response = chain.proceed(chain.request());
             int maxAge = 60; // read from cache for 60 seconds even if there is internet connection
             return response.newBuilder()
                     .header("Cache-Control", "public, max-age=" + maxAge)
                     .removeHeader("Pragma")
                     .build();
         }
     };
    
  • 步骤2:(创建离线拦截器)(仅当您想要离线时缓存访问时)

        static Interceptor offlineInterceptor= new Interceptor() {
        @Override
         public okhttp3.Response intercept(Chain chain) throws IOException {
         Request request = chain.request();
         if (!isInternetAvailable()) {
             int maxStale = 60 * 60 * 24 * 30; // Offline cache available for 30 days 
             request = request.newBuilder()
                     .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                     .removeHeader("Pragma")
                     .build();
           }
           return chain.proceed(request);
        }
      };
    
  • 第三步:(创建缓存对象)

     int cacheSize = 10 * 1024 * 1024; // 10 MB
     Cache cache = new Cache(context.getCacheDir(), cacheSize);
    
  • 步骤4:(将拦截器和缓存添加到OKHTTPClient对象)

         OkHttpClient okHttpClient = new OkHttpClient.Builder()
      // .addInterceptor(provideHttpLoggingInterceptor()) // For HTTP request & Response data logging
         .addInterceptor(OFFLINE_INTERCEPTOR)
         .addNetworkInterceptor(ONLINE_INTERCEPTOR)
         .cache(cache)
         .build();
    
  • 步骤5:(如果您使用Retrofit,请添加OKHTTPClient对象)

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

DONE!


解决方案2:(只需使用一个库即可为您完成所有这些工作!但要应对限制)

Use 好的缓存控件 https://github.com/ncornette/OkCacheControl library

  • 步骤1(如上所示创建Cache对象)

  • 步骤2(创建OKHTTPClient对象)

          OkHttpClient okHttpClient = OkCacheControl.on(new OkHttpClient.Builder())
          .overrideServerCachePolicy(1, MINUTES)
          .forceCacheWhenOffline(networkMonitor)
          .apply() // return to the OkHttpClient.Builder instance
        //.addInterceptor(provideHttpLoggingInterceptor())
          .cache(cache)
          .build();
    
  • 步骤3:(将OKHTTPClient对象附加到Retrofit,如上所示)

  • 步骤4:(创建NetworkMonitor对象)

        static OkCacheControl.NetworkMonitor networkMonitor=new 
        OkCacheControl.NetworkMonitor() {
        @Override
         public boolean isOnline() {
         return isInternetAvailable();
        }
       };
    

DONE!


Testing:为了了解您的设备是从网络还是从缓存获取数据,只需将以下代码添加到您的onResponse改造方法。

 public void onResponse(Call<List<RetroPhoto>> call, Response<List<RetroPhoto>> response) {
            if (response.raw().cacheResponse() != null) {
                Log.e("Network", "response came from cache");
            }

            if (response.raw().networkResponse() != null) {
                Log.e("Network", "response came from server");
            }
        }

如果设备正在使用网络,您将收到“来自服务器的响应”。

如果设备正在使用缓存,您将收到上述两个响应!有关此的更多信息,请阅读此内容article https://futurestud.io/tutorials/retrofit-2-check-response-origin-network-cache-or-both.


有关使用 OKHTTP 拦截器的更多信息,请访问此page https://github.com/square/okhttp/wiki/Interceptors.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

改造 OKHTTP 离线缓存不起作用 的相关文章

  • onBeaconServiceConnect 未调用

    和以前一样 我使用 Android Beacon 库 它已经工作了 我可以通过 BLE 低功耗蓝牙找到信标 但是现在 更新到最新版本的库后 现在方法onBeaconServiceConnect 不再跑了 请告诉我我需要做什么才能让它发挥作用
  • 使用 google Directions API 的地图视图绘制方向 - 解码折线

    我正在尝试使用 Google 方向 API 在我的地图视图上显示方向 但我在从 JSON 响应获取数据时遇到困难 我可以获得 级别 和 点 字符串 但无法弄清楚如何将它们解码为地图上的点 任何帮助将非常感激 我有一个类可以为您解码它们 添加
  • Android:使用 OAuth 访问 google 任务时出现问题

    由于 google 任务没有公共 api 我想编写解决方法并像浏览器一样请求数据 然后解析结果以进一步显示 为了访问数据 我使用 google 实现了 OAuth 身份验证来访问此 url https mail google com htt
  • Manifest Merger工具:替换失败

    我正在使用一个使用自己的 android theme 的库 因此在构建时收到以下错误 错误 55 9 任务 contacit processDebugManifest 执行失败 清单合并失败 AndroidManifest xml 中的属性
  • 带操作按钮的颤动本地通知

    我在我的 flutter 项目中尝试了 flutter 本地通知插件 它在简单通知上工作正常 但我需要带有操作按钮的通知功能 请帮助我或建议我实现此功能 不幸的是 flutter local notifications 插件尚不支持操作按钮
  • 如何在React Native Android中获取响应头?

    您好 我想在获取 POST 请求后获取响应标头 我尝试调试看看里面有什么response with console log response 我可以从以下位置获取响应机构responseData但我不知道如何获取标题 我想同时获得标题和正文
  • 出现错误错误:res/menu/mainMenu.xml:文件名无效:必须仅包含[a-z0-9_。]

    我是安卓新手 刚刚开始使用 我在 res 文件夹中创建了一个文件 menu mainMenu xml 但我得到了错误 Error res menu mainMenu xml invalid file name must contain on
  • AudioTrack、SoundPool 或 MediaPlayer,我应该使用哪个?

    如果我需要能够 播放多个音频文件 具有不同的持续时间 例如 5 到 30 秒 独立设置右 左声道的音量 应用声音效果 如混响 失真 那么 我应该使用哪个 API 另外 我在 AudioTrack API 上找不到太多文档 有谁知道在哪里可以
  • Android 服务是否有办法检测设备何时锁定?

    我有一个 Android 服务 我希望在设备锁定时执行操作 我想澄清一下 我对屏幕开 关状态不感兴趣 我知道如何使用带有 Intent ACTION USER PRESENT 和 KeyguardManager inKeyguardRest
  • Android 应用程序中的 Eszett (ß)

    我的 res layout activity 文件中的德语 字符在我的应用程序中自动转换为 ss 即使我将语言和键盘设置为德语 它仍然不会显示 Android 中可以显示 吗 edit
  • 如何检查 Android 中连接的 wifi 网络是否处于活动状态

    如何自动检查android中连接的WiFi网络上的互联网是否处于活动状态 我可以检查 wifi 是否已启用或 wifi 网络是否已连接 但我不确定如何检查互联网是否已连接 这可能吗 private boolean connectionAva
  • Java 文件上传速度非常慢

    我构建了一个小型服务 它从 Android 设备接收图像并将其保存到 Amazon S3 存储桶中 代码非常简单 但是速度非常慢 事情是这样的 public synchronized static Response postCommentP
  • 将 Firebase 云消息传递与 Windows 应用程序结合使用

    我在 Android 和 iOS 应用程序中使用 Firebase Cloud Messaging 但是我还有此应用程序的 Windows Mac OS 版本 我想保留相同的逻辑 我知道 Firebase Cloud Messaging 可
  • Android 相机未保存在特定文件夹 [MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA]

    当我在 Intent 中使用 MediaStore INTENT ACTION STILL IMAGE CAMERA 时遇到问题 相机正常启动 但它不会将文件保存在我的特定文件夹 photo 中 但是当我使用 MediaStore ACTI
  • HERE 地图:更改路线已行驶部分的颜色

    导航时可以改变路线的颜色吗 具体来说 我希望路线中已行驶的部分的颜色与即将行驶的部分的颜色不同 现在都是同一个颜色 将 MapRoute 对象的 TravelColor 变量设置为透明对我来说很有效 mapRoute color Resou
  • Android 后台服务示例,具有交互式调用方法

    我不是 Android 方面的专家 我正在寻找一个 Android 应用程序的示例 该应用程序使用一个服务 其中有真正的功能方法 或者换句话说 一个服务可以用来做什么 我们什么时候需要它 超越简单的东西服务举例 我确信您渴望获得一些工作代码
  • 有关 ListView 自定义行布局项目上的 onClick() 事件的帮助

    我有一个 ListView 其行由我格式化 每行都有 ImageView 和 TextView 的混合 我还实现了自己的适配器 并且能够通过它绘制每一行 现在 我想要这样的东西 用户单击 ImageView 不是行上的其他任何位置 但只有此
  • 哪些 Flutter 插件或功能可以利用外部 iOS/Android 显示器来显示与主显示器不同的内容

    我正在构建一个跨平台应用程序 需要在外部显示器上显示不同的视图 通常通过连接到 LCD 投影仪的 HDMI 适配器电缆连接 Flutter 是否能够在内置的外部显示器上显示不同的屏幕 在现有的 Flutter 插件中还是使用现有的 Flut
  • Android:监听状态栏通知

    有没有办法在状态栏被下拉时监听通知 1 用于检测状态栏变化 您可以注册一个监听器来获取系统UI可见性变化的通知 因此 要在您的活动中注册侦听器 Detecting if the user swipe from the top down to
  • 在Android Studio gradle项目中使用NDK和STL

    我在将 stlport 链接到 Android Studio 中的 gradle 项目时遇到问题 使用 NDK 的 Eclipse Android 项目迁移到 Android Studio 该项目使用 STL 我有包含内容的 android

随机推荐

  • Django 中的双外键?

    有没有办法在 Django 中模拟双外键 例如 如果我有表格 音频 覆盖 html 表 timeline item 它有一个字段 id 以及一个指定音频 覆盖或 html 的字段类别 有谁知道我将如何在 Django 中对此进行建模 或者是
  • 将选择结果转换为插入脚本 - SQL Server

    我有 SQL Server 2008 SQL Server Management Studio 我需要从一个数据库中的表中选择数据并将其插入到另一个数据库中的另一个表中 如何将我的选择返回的结果转换为INSERT INTO 评论澄清 虽然我
  • 如何复制 Core Data 托管对象?

    我有一个托管对象 A 其中包含各种属性和关系类型 并且它的关系也有自己的属性和关系 我想做的是 复制 或 复制 以对象 A 为根的整个对象图 从而创建一个与 A 非常相似的新对象 B 更具体地说 B 或其子级 包含的任何关系都不应指向与 A
  • 使用水平滚动条滚动面板

    如何将水平滚动条应用于面板 我正在开发二维平铺地图编辑器 并使用面板来保存内容 当我的地图比面板大时 它就会消失并变得不可见 是否可以应用自定义滚动条 The Panel http msdn microsoft com en us libr
  • 如何在c#中查找名称以某个字符串开头的HTML元素的数量?

    我不确定该主题是否很好地描述了我的问题 但我正在使用 jQuery 动态创建一些 HTML 元素 文本框 而且我永远不知道将创建多少个元素 它循环通过数据库 然后我想获取后面代码中的所有元素并执行一些操作 将它们插入到另一个数据库中 我知道
  • 类型映射资源以及将列表转换为向量(以及反之)

    我正在使用 SWIG 将 c 包装在 python 中 并且需要使用类型映射以使我的 python 脚本尽可能简单 作为第一次尝试 我只是发送 2 个列表 将它们转换为向量 将两个向量相加 然后将结果返回到新列表中 我的问题是 我发现 SW
  • 使用 c++ 17 更新时 G++ 编译器警告

    我正在 Windows 10 上使用 mingw 运行 g 编译器 在 cmd 中检查编译器版本时 我得到以下信息 g version g MinGW org GCC Build 2 9 2 0 与c version 当我使用结构化绑定编译
  • jQuery 未在附加项目上触发

    我在触发已添加到页面的项目上的事件时遇到问题 在以下示例中 如果单击 X 它将删除其中一项 但如果添加一项 则无法将其删除 清单如下 ul li Item 1 a class remove X a li li Item 2 a class
  • 如何对齐 2 个反应原生元素,1 个位于中心,1 个位于开头

    假设我们有这些反应原生样式 var styles StyleSheet create parentView width 400 height 150 containerView flex 1 flexDirection row alignI
  • 每当 SwiftUI 中更新 CoreData 时更新状态变量

    从 CoreData 更新一些数据后 我还想更新一个State 可变为返回结果的数量 当CoreData改变时 Stepper应始终设置为返回结果的数量 然而 onAppear当我使用时也会发生火灾Stepper 我怎样才能办理登机手续on
  • 如何在ListView上使用Button的onClickListener方法

    我有一个习惯ListView其中包含一个Button 该按钮的功能是删除按钮 每当用户单击此按钮时 当前行将被删除 我该怎么做 我该如何设置onClickListener对于这个按钮 如何捕获该按钮所在的行 ID 提前致谢 In your
  • SAM Local 似乎没有运行授权者功能

    我刚刚开始使用SAM 本地 https github com thoeni aws sam local 但在尝试为我的端点配置授权者功能时遇到了问题 我一直在看主要 SAM 文档 https github com awslabs serve
  • 引用的错误类型不是注释类型:

    我得到了以下方面 Around execution public DisabledForBlockedAccounts annotation denyForTeam public Object translateExceptionsDeny
  • 在jsFiddle中引用GitHub文件

    是否有可能滥用从github存储库中获取文件作为jsFiddle中的外部资源 TLDR Visit rawgit com http rawgit com 这将直接从 GitHub 将您的文件弹出到 CDN 上 以便您可以使用它们 不幸的是
  • 模拟 6502 个时钟周期的最佳方法?

    我一直在研究 NES 模拟器的 CPU 我想知道管理多周期指令周期的最佳方法是什么 假设实现了一个周期的时间延迟 我可以根据指令的每个步骤单独增加周期 如下所示 void EXAMPLE INSTRUCTION step1 cycles s
  • 对形状内的元素进行聚类

    我见过这个解决方案 http jsfiddle net srvikram13 CLs8P 它似乎负责在 一致 形状内对元素进行聚类 而不会重叠 但是如果形状更加模糊怎么办 如下所示 我的前几次尝试似乎是将形状简化为其最基本的形式 然后检查元
  • 通过 setuptools 在 PyPi 上发布作者的正确方法

    我目前使用setuptools构建我的 Python 包 我已经在我的文件中以这种方式声明了两位作者pyproject toml file authors name X Y email email protected cdn cgi l e
  • iOS WebApp 不显示启动图像

    I added 在我网站的 head 标签之间 但它不适用于启动图像 这是正常的吗 您是否尝试在桌面浏览器中查看启动图像 如果是这样 那么我会在 IOS 移动设备上访问您的网站并将该应用程序添加到您的主屏幕 当您打开它时 您应该会看到启动屏
  • 错误 A2070:指令操作数无效

    错误位于 AfterLoop skope 行 mov esi edi 中 我该如何解决这个问题 节点删除函数 headptr nodeToremove removeNode proc headPtr 8 nodeToRemove headP
  • 改造 OKHTTP 离线缓存不起作用

    我阅读了数十个教程和 Stackoverflow 来解决我的问题 但没有任何效果对我有用 而且 它们中的大多数都很旧 所以 OKHTTP 可能以某种方式发生了变化 我想要的只是启用离线缓存用于改造 我正在使用 GET 我尝试仅使用offli