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

Crashlytics上报Constant Dns.java第5行错误:OkHttp后台静置后调API报UnknownHostException

故障诱因
  • Android系统默认DNS解析存在虚拟机层面的缓存,缓存过期后如果后台发生过网络状态切换(比如WiFi断连重连、蜂窝网络切换基站),系统返回的DNS解析结果会携带EAI_NODATA错误,且该错误结果会被短期缓存,导致连续解析失败
  • OkHttp默认的retryOnConnectionFailure(true)配置仅会重试连接、读写阶段的错误,不会重试DNS解析阶段抛出的UnknownHostException,解析失败直接抛出异常
  • 你自定义的Dispatcher线程池配置了固定20个核心线程,核心线程默认不会被回收,长期存活的线程会持有旧的网络上下文,APP切后台静置后旧上下文失效,线程复用的时候依旧使用失效的上下文发起DNS解析,导致持续报错
  • 部分Android版本的系统DNS实现存在bug,后台休眠后DNS解析器实例会被系统回收,复用旧实例就会直接返回解析失败
修复方案

1. 实现带重试逻辑的自定义DNS

替换OkHttp默认的系统DNS,解析失败时自动重试,避免单次解析失败直接抛出异常:

class RetryDns(private val maxRetry: Int = 3) : Dns {
    override fun lookup(hostname: String): List<InetAddress> {
        var lastError: Exception? = null
        repeat(maxRetry) {
            try {
                return Dns.SYSTEM.lookup(hostname)
            } catch (e: UnknownHostException) {
                lastError = e
                Thread.sleep(100)
            }
        }
        throw lastError ?: UnknownHostException("DNS解析失败,已重试$maxRetry 次")
    }
}

在OkHttpClient构建时添加配置:

OkHttpClient.Builder()
    .dns(RetryDns())
    // 其余配置不变

2. 优化Dispatcher线程池配置

没有特殊需求的话直接使用OkHttp默认的Dispatcher即可。如果必须自定义线程池,修改配置允许线程空闲后回收,避免线程长期持有旧网络上下文:

// 核心线程数设为0,所有线程空闲60秒后自动回收
val executorService: ExecutorService = ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, LinkedBlockingQueue())
val myDispatcher = Dispatcher(executorService)

3. 添加DNS失败重试拦截器

增加拦截器主动捕获DNS解析异常,触发重试逻辑:

class DnsRetryInterceptor(private val maxRetry: Int = 2) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var retryCount = 0
        var exception: Exception? = null
        while (retryCount < maxRetry) {
            try {
                return chain.proceed(chain.request())
            } catch (e: UnknownHostException) {
                exception = e
                retryCount++
                Thread.sleep(200)
            }
        }
        throw exception ?: IOException("请求失败")
    }
}

在OkHttpClient构建时添加该拦截器即可。

4. 监听网络变化清理缓存

注册系统网络状态监听,网络切换时主动清空OkHttp连接池,避免复用旧连接:

// 网络变化时调用
okHttpClient.connectionPool.evictAll()
okHttpClient.dispatcher.cancelAll()

内容的提问来源于stack exchange,提问作者Sarthak Gandhi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 22:45:08