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

OkHttp 3响应缓存异常:离线触发java.net.UnknownHostException

解决OkHttp离线缓存失效的问题

看起来你遇到的问题是明明配置了缓存,但离线时还是无法加载缓存数据,反而抛出了域名解析失败的错误。我来帮你分析下问题所在,然后一步步解决:

问题1:缓存目录配置错误

你现在用new File("cachedirectory")创建缓存目录,这个相对路径在Android环境下可能没有正确的写入权限,甚至根本创建失败,直接导致缓存没被保存下来。

修复方案:改用应用的内部缓存目录,既安全又能保证写入权限:

Cache provideOkHttpCache() {
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    // 使用App内部专属缓存目录
    File cacheDir = new File(getApplicationContext().getCacheDir(), "http-cache");
    Cache cache = new Cache(cacheDir, cacheSize);
    return cache;
}

问题2:缓存拦截器的类型与逻辑有误

你当前用addInterceptor(应用拦截器)修改响应头,但这类拦截器会在所有请求流程中执行,包括离线缓存读取的场景。而且你没有区分有网络/无网络的缓存策略,导致离线时OkHttp还是尝试发起网络请求,最终报错。

正确的做法是用两个拦截器分工:

  • 应用拦截器:处理离线场景,告诉OkHttp优先读取缓存
  • 网络拦截器:仅在有网络时修改响应头,设置缓存有效期

调整拦截器代码:

// 网络拦截器:仅在网络请求成功返回时,设置缓存有效期
Interceptor cacheNetworkInterceptor = new Interceptor() {
    @Override
    public okhttp3.Response intercept(Chain chain) throws IOException {
        okhttp3.Response originalResponse = chain.proceed(chain.request());
        // 有网络时,缓存有效期设为1天
        return originalResponse.newBuilder()
                .header("Cache-Control", "public, max-age=86400")
                .build();
    }
};

// 应用拦截器:处理离线场景,强制读取缓存
Interceptor cacheAppInterceptor = new Interceptor() {
    @Override
    public okhttp3.Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        // 无网络时,设置请求仅读取缓存,允许最长28天的过期缓存
        if (!isNetworkAvailable(getApplicationContext())) {
            request = request.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=2419200")
                    .build();
        }
        return chain.proceed(request);
    }
};

// 新增网络状态判断方法
private boolean isNetworkAvailable(Context context) {
    ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}

然后在构建OkHttpClient时,注意拦截器的添加顺序:

OkHttpClient okHttpClient = new OkHttpClient()
        .newBuilder()
        .addInterceptor(httpLoggingInterceptor)
        .addInterceptor(cacheAppInterceptor) // 应用拦截器优先添加
        .addNetworkInterceptor(cacheNetworkInterceptor) // 网络拦截器后添加
        .cache(cache)
        .build();

问题3:必要权限缺失(必查)

如果你的应用运行在Android 6.0及以上系统,记得在AndroidManifest.xml中添加网络权限:

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

验证缓存是否生效

你可以在onResponse中打印日志,确认缓存读取状态:

Log.v("cache-control", "是否来自缓存: " + isFromCache(response));
Log.v("cache-control", "缓存响应: " + response.raw().cacheResponse());
Log.v("cache-control", "网络响应: " + response.raw().networkResponse());

如果离线时cacheResponse不为null、networkResponse为null,就说明缓存生效了。

按照上面的步骤修改后,再重新测试离线场景:在线运行保存缓存→关闭应用开飞行模式→重启应用,就能正常加载缓存数据了。

内容的提问来源于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 07:52:42