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" />
为什么之前的代码不生效?
再帮你梳理下根源:
NetworkInterceptor离线时不执行,导致你的离线响应头修改逻辑完全没机会运行;- 离线时没有在请求阶段禁止网络请求,OkHttp还是会尝试发起请求,失败后直接走
onFailure; - 在线时的
maxAge(0)虽然会让OkHttp去服务器验证缓存,但用FORCE_NETWORK更直接符合你“强制从服务器拉取”的需求。
调整后,在线时会强制拉取服务器数据并更新缓存,离线时会直接读取缓存,不会再触发onFailure了。
内容的提问来源于stack exchange,提问作者Anton Prokopov
相关产品推荐
相关产品推荐

