You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit离线缓存失效求助:无法触发离线缓存逻辑

问题分析与修复方案

嘿,我帮你找到问题所在了!你的代码核心问题出在拦截器的类型选择和缓存策略的时机上,咱们一步步来拆解修复:

1. 拦截器类型用错了

你用addNetworkInterceptor()添加了缓存拦截器,但OkHttp的NetworkInterceptor有个关键特性:只有当设备有网络连接、真正发起网络请求时才会被调用。当设备离线时,这个拦截器根本不会执行,所以你写的离线代码块永远没机会触发,这是最核心的问题。

你需要把拦截器改成用addInterceptor()(应用拦截器),它会在所有请求流程中被调用——不管设备有没有网络,都能走到你的缓存逻辑里。

2. 缓存策略的时机搞反了

你现在的逻辑是在响应阶段才给离线情况设置only-if-cached头,但其实应该在请求阶段就告诉OkHttp:“如果离线,就只用缓存,别尝试发网络请求”。否则离线时OkHttp还是会硬着头皮发请求,失败后就直接进入onFailure回调了。

3. 修复后的完整代码示例

第一步:修改OkHttpClient的构建

把addNetworkInterceptor换成addInterceptor:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(httpLoggingInterceptor)
        .addInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR) // 替换成应用拦截器
        .cache(provideOkHttpCache())
        .build();

第二步:调整拦截器逻辑

把离线时的请求头设置提前,同时简化缓存控制的写法:

Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
    @Override
    public okhttp3.Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        
        // 离线时:直接告诉OkHttp只用缓存,别发网络请求
        if (!isOnline()) {
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE) // 等价于public, only-if-cached, max-stale=xxx
                    .build();
        } else {
            // 在线时:强制从服务器拉取,不使用缓存
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_NETWORK)
                    .build();
        }

        okhttp3.Response originalResponse = chain.proceed(request);
        
        // 响应阶段:设置缓存有效期,给离线场景留后路
        if (isOnline()) {
            // 在线时,设置缓存的短期有效期(这里设1分钟,可按需调整)
            int maxAge = 60;
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, max-age=" + maxAge)
                    .removeHeader("Pragma") // 清除旧的Pragma头,避免冲突
                    .build();
        } else {
            // 离线时,允许缓存过期4周也能使用
            int maxStale = 60 * 60 * 24 * 28;
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .removeHeader("Pragma")
                    .build();
        }
    }
};

4. 额外注意事项

  • 确保你的isOnline()方法能准确判断网络状态,比如用ConnectivityManager实现:
private boolean isOnline() {
    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = cm.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}
  • 别忘了在AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

为什么之前的代码不生效?

再帮你梳理下根源:

  1. NetworkInterceptor离线时不执行,导致你的离线响应头修改逻辑完全没机会运行;
  2. 离线时没有在请求阶段禁止网络请求,OkHttp还是会尝试发起请求,失败后直接走onFailure;
  3. 在线时的maxAge(0)虽然会让OkHttp去服务器验证缓存,但用FORCE_NETWORK更直接符合你“强制从服务器拉取”的需求。

调整后,在线时会强制拉取服务器数据并更新缓存,离线时会直接读取缓存,不会再触发onFailure了。

内容的提问来源于stack exchange,提问作者Anton Prokopov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:25:12