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

Android应用Retrofit2网络异常求助:无法解析主机/连接失败

周期性Retrofit2网络连接问题(连接超时/主机解析失败)

我开发的Android应用基于Retrofit2 2.9.0版本,已获取全部必要权限,通过移动数据网络每隔15分钟同步自有服务器及api.openweathermap.org的数据。近期周期性出现两类网络连接错误:

1. 连接超时失败

failed to connect to xxxxxx.com/xxx.xxx.xxx.xxx (port 443) from /192.0.0.4 (port 54944) after 30000ms
libcore.io.IoBridge.connectErrno(IoBridge.java:190)
libcore.io.IoBridge.connect(IoBridge.java:134)
java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
java.net.Socket.connect(Socket.java:631)
okhttp3.internal.platform.Platform.connectSocket(Platform.kt:128)
okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.kt:295)
okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:207)
okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
java.lang.Thread.run(Thread.java:923)

2. 无法解析主机地址

Unable to resolve host "xxxxxx.com": No address associated with hostname
java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:156)
java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103)
java.net.InetAddress.getAllByName(InetAddress.java:1152)
okhttp3.Dns$Companion$DnsSystem.lookup(Dns.kt:49)
okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(RouteSelector.kt:164)
okhttp3.internal.connection.RouteSelector.nextProxy(RouteSelector.kt:129)
okhttp3.internal.connection.RouteSelector.next(RouteSelector.kt:71)
okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:205)
okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
java.lang.Thread.run(Thread.java:923)
libcore.io.Linux.android_getaddrinfo(Native Method)
libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:73)
libcore.io.BlockGuardOs.android_getaddrinfo(BlockGuardOs.java:202)
libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:73)
java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:135)
java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103)
java.net.InetAddress.getAllByName(InetAddress.java:1152)
okhttp3.Dns$Companion$DnsSystem.lookup(Dns.kt:49)
okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(RouteSelector.kt:164)
okhttp3.internal.connection.RouteSelector.nextProxy(RouteSelector.kt:129)
okhttp3.internal.connection.RouteSelector.next(RouteSelector.kt:71)
okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:205)
okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
java.lang.Thread.run(Thread.java:923)

已将OkHttpClient超时时间调整至30秒,但问题仍未解决。出现问题时浏览器可正常加载对应网页,排除服务器端问题,现寻求可行解决方案。


解决方案

针对主机解析失败(Unable to resolve host)

  • 自定义DNS解析器:移动网络下系统DNS可能存在缓存或解析异常,自定义Dns实现优先使用IPv4,或添加备用解析逻辑:
val customDns = Dns { hostname ->
    try {
        // 优先过滤出IPv4地址,避免IPv6解析失败
        InetAddress.getAllByName(hostname)
            .filterIsInstance<Inet4Address>()
            .ifEmpty { InetAddress.getAllByName(hostname) }
            .toList()
    } catch (e: Exception) {
        // 备用逻辑:直接返回系统解析结果
        InetAddress.getAllByName(hostname).toList()
    }
}

// 构建OkHttpClient时配置
val client = OkHttpClient.Builder()
    .dns(customDns)
    .build()
  • 禁用IPv6优先:在应用启动时添加以下代码,强制系统优先使用IPv4:
System.setProperty("java.net.preferIPv6Addresses", "false");

针对连接超时失败

  • 自定义重试拦截器:针对连接超时、DNS解析失败这类可恢复异常,增加重试次数并采用指数退避策略:
class RetryInterceptor(private val maxRetryCount: Int = 3) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        var retryCount = 0
        var lastException: IOException? = null

        while (retryCount <= maxRetryCount) {
            try {
                val response = chain.proceed(request)
                if (response.isSuccessful) {
                    return response
                }
                response.close()
            } catch (e: IOException) {
                lastException = e
                // 判断是否为可重试异常
                if (e is ConnectException || e is UnknownHostException) {
                    retryCount++
                    Thread.sleep(1000 * retryCount) // 指数退避等待
                    continue
                }
                throw e
            }
        }
        throw lastException ?: IOException("Max retry attempts reached")
    }
}

// 添加到OkHttpClient
val client = OkHttpClient.Builder()
    .addInterceptor(RetryInterceptor())
    .build()
  • 优化连接池配置:延长空闲连接存活时间,适配移动网络的不稳定特性:
val connectionPool = ConnectionPool(
    maxIdleConnections = 5,
    keepAliveDuration = 5,
    timeUnit = TimeUnit.MINUTES
)

val client = OkHttpClient.Builder()
    .connectionPool(connectionPool)
    .build()
  • 前置网络可用性检测:发起同步请求前,先验证移动网络是否真正可用(不仅是连接状态,还要能访问公网):
fun isNetworkAvailable(context: Context): Boolean {
    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val network = connectivityManager.activeNetwork ?: return false
    val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
    // 验证是否为移动数据且能访问互联网
    return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) 
            && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}

其他优化点

  • 添加请求间隔随机偏移:将固定15分钟的同步间隔改为14-16分钟的随机值,避免批量请求导致网络拥堵。
  • 升级OkHttp版本:Retrofit2.9.0适配的OkHttp版本为4.9.0,升级到最新稳定版(如4.x系列),可修复部分移动网络下的连接bug。
  • 排查系统网络日志:通过adb logcat | grep -i network查看系统层面的网络错误,确认是否为系统网络栈问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:25:01