
本文详解 retrofit 在 android 中出现 “unable to resolve host” 和 “failed to connect” 错误的根本原因、典型场景及系统性解决方案,涵盖权限配置、dns 行为、okhttpclient 优化与网络状态容错设计。
本文详解 retrofit 在 android 中出现 “unable to resolve host” 和 “failed to connect” 错误的根本原因、典型场景及系统性解决方案,涵盖权限配置、dns 行为、okhttpclient 优化与网络状态容错设计。
在使用 Retrofit 2.9.0 进行周期性网络同步(如每 15 分钟调用自有服务或 OpenWeatherMap API)时,开发者常遇到两类高频错误:
-
Unable to resolve host "xxx.com": No address associated with hostname failed to connect to xxx.com/xx.xx.xx.xx (port 443) ... after 30000ms
值得注意的是:即使设备浏览器可正常访问网页,App 仍报错——这明确指向非全局断网问题,而是 Android 应用层网络栈的特定行为。
? 首要检查:Manifest 权限是否完备?
最基础却极易被忽略的一环是 INTERNET 权限缺失。即使应用 Target SDK ≥ 31(Android 12+),该权限仍为 普通权限(normal permission),但必须显式声明,否则所有网络请求将静默失败(不抛异常,但底层 socket 创建直接被系统拦截)。
✅ 正确配置(置于 AndroidManifest.xml 的 <manifest></manifest> 根节点内):
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
⚠️ 注意:
-
ACCESS_NETWORK_STATE(用于检测网络状态)是可选辅助权限,不解决连接本身问题; -
INTERNET权限无需运行时申请,但若遗漏,OkHttpClient会因系统拒绝 socket 创建而触发上述堆栈中的IoBridge.connectErrno或Inet6AddressImpl.lookupHostByName失败。
? 深层原因解析:DNS 与连接失败的本质区别
| 错误类型 | 触发阶段 | 典型原因 | 是否与网络质量强相关 |
|---|---|---|---|
Unable to resolve host |
DNS 查询阶段 | DNS 缓存污染、IPv6/IPv4 协商失败、私有 DNS(如 Private DNS / DNS over TLS)干扰、运营商 DNS 不稳定 | ❌ 否(DNS 层面故障,与带宽/延迟无关) |
failed to connect |
TCP 握手阶段 | 目标端口不可达、防火墙拦截、SSL/TLS 握手超时、服务器临时不可用、OkHttp 连接池复用脏连接 | ⚠️ 弱相关(可能由弱网引发,但更可能是服务端或中间链路问题) |
? 小知识:Android 10+ 默认启用
dnsResolver,对 DoT(DNS over TLS)支持增强,但也可能因 DNS 服务器响应慢或返回空记录导致getAllByName()返回空数组,进而抛出UnknownHostException。
?️ 实战优化方案
1. 构建健壮的 OkHttpClient(推荐 Retrofit 2.9+ 配置)
val client = OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
// ✅ 启用系统 DNS + 自定义备用 DNS(防止单点故障)
.dns(Dns.SYSTEM.withFallback())
// ✅ 禁用 IPv6(若服务端未良好支持,可规避地址解析歧义)
.ipResolver { inetAddresses ->
inetAddresses.filter { it.isIPv4Address }
}
// ✅ 启用连接池复用,但设合理上限防资源耗尽
.connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.openweathermap.org/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
✅
Dns.SYSTEM.withFallback()是 OkHttp 4.9+ 提供的扩展方法(需引入okhttp-dnsoverhttps或自定义实现),若无法升级,可手动封装 DNS 回退逻辑。
2. 主动网络状态与可达性检测(非仅 ConnectivityManager)
单纯判断 NetworkInfo.isConnected() 不足以保证 HTTP 可达。建议增加轻量探测:
suspend fun isServerReachable(host: String): Boolean {
return try {
withContext(Dispatchers.IO) {
InetAddress.getByName(host).isReachable(3000) // ICMP ping(需注意部分 Android 版本限制)
}
true
} catch (e: Exception) {
Log.w("Network", "Host $host unreachable", e)
false
}
}
更可靠的方式是发起一次 HEAD 请求(无响应体,低开销):
val call = client.newCall(Request.Builder().url("https://$host/").head().build())
return try {
call.execute().use { it.isSuccessful }
} catch (e: Exception) { false }
3. Retrofit Call 层容错:重试 + 降级
val call = apiService.fetchWeather(...)
call.enqueue(object : Callback<weatherresponse> {
override fun onFailure(call: Call<weatherresponse>, t: Throwable) {
when (t) {
is UnknownHostException -> {
// DNS 失败:提示“域名解析异常”,可切换备用域名或稍后重试
showNetworkError("域名无法解析,请检查网络设置")
}
is SocketTimeoutException -> {
// 连接/读写超时:可自动重试 1 次(避免雪崩)
if (!isRetrying) retryRequest()
}
else -> {
// 其他异常(SSLHandshakeException 等)按需处理
}
}
}
// ...
})</weatherresponse></weatherresponse>
✅ 总结:排查清单
- [ ]
AndroidManifest.xml中已声明<uses-permission android:name="android.permission.INTERNET"></uses-permission> - [ ] 使用
OkHttpClient.Builder显式设置合理超时(非仅 Retrofit 层) - [ ] 检查是否启用了 Private DNS(设置 → 网络与互联网 → 私有 DNS),临时关闭验证是否复现
- [ ] 在真机弱网环境(如 Airplane Mode + Wi-Fi 开启但无互联网)下复现并抓包(
adb shell ping,adb shell getprop net.dns1) - [ ] 对关键接口添加 HEAD 探活 + 本地缓存降级策略,提升用户体验鲁棒性
通过以上结构化排查与配置优化,90% 以上的 Unable to resolve host 与 failed to connect 问题均可定位并解决,显著提升 Android 应用在网络边缘场景下的稳定性。











