Android 网络请求总超时?OkHttp + Retrofit 排查与稳定性优化实战

作者: Android学习网 分类: Android网络编程 发布时间: 2026-03-21 00:23

先说结论

Android 网络请求超时,大多数时候不是接口慢这么简单。真正在项目里最常见的,是超时参数配得不对、DNS 偶发抖动、连接池复用异常、弱网下重试策略粗暴、日志采样不足。你如果一上来只盯服务端日志,通常会绕很久。

我平时排这类问题,顺序基本固定:先区分到底是连接超时、读取超时还是写入超时;再确认是不是某个运营商、某个机型、某个网络环境更容易触发;最后才去调参数和补兜底。这样排,效率最高。

这篇就按实战顺序来,不讲空话,直接把一套能落地的 Android 网络超时排查和稳定性优化流程写清楚。

一、先把超时类型分清楚

很多项目里一看到报错里有 timeout,就统一叫网络超时。这说法太粗了。OkHttp 里至少有三种典型超时:连接超时、读取超时、写入超时。它们对应的问题根本不是一回事。

val client = OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(15, TimeUnit.SECONDS)
    .writeTimeout(15, TimeUnit.SECONDS)
    .callTimeout(20, TimeUnit.SECONDS)
    .build()

connectTimeout 重点看建连阶段,比如 DNS 解析慢、TCP 握手异常、代理链路抖动。readTimeout 更像是服务端迟迟不回包,或者回包速度太慢。writeTimeout 常见在上传场景,比如图片、音视频、日志批量上报。

我一般第一步就会把异常类型按这三类分开统计。你如果不先分,后面所有优化动作都会混在一起,最后得到的结论八成是错的。

二、先看客户端配置

Android 项目里最常见的坑之一,是把 OkHttp 和 Retrofit 当成接上就行的黑盒。结果开发环境没问题,一到线上弱网就开始炸。

  • 是否为所有接口共用一个 OkHttpClient
  • 是否有人在不同模块里各自 new 了不同 client
  • 超时参数是不是统一且合理
  • 是否把日志拦截器开到了生产高频接口
  • 是否启用了错误的重试逻辑
val logging = HttpLoggingInterceptor().apply {
    level = if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor.Level.BASIC
    } else {
        HttpLoggingInterceptor.Level.NONE
    }
}

val client = OkHttpClient.Builder()
    .retryOnConnectionFailure(true)
    .addInterceptor(logging)
    .connectionPool(ConnectionPool(8, 5, TimeUnit.MINUTES))
    .build()

这里有个经验值:生产环境别开 BODY 级日志,尤其是高频接口。你以为是在排查问题,实际上可能在制造更大的 I/O 压力。

三、排查 DNS 和建连链路

如果报错集中在连接阶段,我第一反应不是去问后端你们挂了吗,而是先查 DNS 和建连链路。移动网络、公司代理、某些公共 Wi‑Fi,经常在这一步搞事情。

class NetEventListener : EventListener() {
    override fun dnsStart(call: Call, domainName: String) {
        Log.d("NetTrace", "dnsStart: $domainName")
    }

    override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) {
        Log.d("NetTrace", "connectStart: ${inetSocketAddress.hostName}")
    }

    override fun secureConnectStart(call: Call) {
        Log.d("NetTrace", "tlsStart")
    }
}

加完这些埋点,你很快就能看出来问题是卡在 DNS、TCP,还是 TLS。很多请求超时最后根本不是接口逻辑问题,而是 DNS 抽风或者 TLS 握手慢得离谱。

四、常见坑:重试逻辑不能乱写

很多人看到偶发超时,第一反应就是多试几次。这个方向不算错,但写法经常有问题。最典型的错误有三个:所有接口无脑重试、POST 也当成幂等请求重试、没有退避策略。

fun shouldRetry(code: Int, method: String): Boolean {
    if (method != "GET") return false
    return code == 502 || code == 503 || code == 504
}

如果你们线上已经因为弱网做了重试,记得把首次请求耗时和重试后总耗时分开统计。否则报表看起来成功率变高了,用户体感可能反而更差。

五、性能和稳定性要拆开看

一出超时就把 readTimeout 从 10 秒改到 30 秒,这是最常见也最偷懒的处理方式。短期看报错少了,长期看会把慢问题彻底藏起来。

  • 性能:接口为什么慢
  • 稳定性:慢的时候客户端怎么兜底

比如首页瀑布流接口,如果后端数据量大、图片多、压缩差,客户端一味调超时没有意义。你该做的是分页、缓存、压缩、降级。

六、工具和验证一定要跟上

  • Charles / Fiddler:看请求是否真正发出,回包长什么样
  • Android Studio Network Inspector:快速看请求分布
  • MockWebServer:本地模拟超时、慢响应、错误码
  • adb 弱网模拟:验证重试和降级逻辑
val server = MockWebServer()
server.enqueue(
    MockResponse()
        .setBody("{"ok":true}")
        .setBodyDelay(8, TimeUnit.SECONDS)
)

你如果从来没用 MockWebServer 模拟过超时场景,很多稳定性方案其实只是脑补。把它本地跑通一遍,心里会踏实很多。

七、最稳的排查顺序

  1. 先分清 connect / read / write timeout
  2. 看问题是否集中在某网络、某地区、某机型
  3. 确认 OkHttpClient 是否统一、参数是否一致
  4. 用 EventListener 拆 DNS、建连、TLS 时间
  5. 审查是否存在错误重试或错误日志策略
  6. 用 MockWebServer 和弱网工具复现
  7. 最后再决定是调超时、改重试、还是改接口设计

常见坑和排查提醒

  • 把所有超时都归类成一个问题,最后什么都查不清
  • 生产环境日志开太大,排查没做成,先把性能拖垮了
  • POST 无脑重试,最后引入重复提交
  • 弱网没做本地模拟,只靠线上猜
  • 超时一高就改 30 秒,慢问题被彻底掩盖

结论 + 下一步

Android 网络请求总超时,真正有效的处理方式不是调大超时,而是先把异常类型拆开,再按链路定位。OkHttp 和 Retrofit 已经给了足够多的钩子,关键在于你有没有按工程化方法去用。

如果下一篇继续往下写,我建议直接补连接池复用异常和弱网降级 UI 设计。前者解决底层波动,后者决定用户体感到底差不差。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注