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
相关产品推荐
相关产品推荐

