1. Retrofit 超时配置原理

1.1 三类超时概念

超时是网络请求健壮性的核心防护机制。Retrofit 底层依赖 OkHttp 执行实际 HTTP 通信,因此超时参数全部通过 OkHttpClient 配置。三类超时各自控制不同阶段:

超时类型 控制阶段 典型默认值(OkHttp) 作用说明
连接超时(connectTimeout) TCP 三次握手阶段 10 秒 客户端发起连接到服务端确认连接所允许的最大等待时间
读取超时(readTimeout) 数据从服务端流向客户端 10 秒 两次数据包之间的最大空闲时间(非总下载时间)
写入超时(writeTimeout) 数据从客户端流向服务端 10 秒 两次发送数据包之间的最大空闲时间(非总上传时间)
经验值:移动端弱网环境建议将连接超时放宽至 15–20 秒,读取超时视接口响应特性单独设置;写入超时在文件上传场景需适当增大(如 60 秒)。

1.2 OkHttpClient 超时参数详解

OkHttpClient 通过 Builder 模式设置超时参数,单位均为毫秒。以下是构造示例及每个参数的含义:

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(15, TimeUnit.SECONDS)      // 连接超时 15 秒
    .readTimeout(20, TimeUnit.SECONDS)         // 读取超时 20 秒
    .writeTimeout(30, TimeUnit.SECONDS)        // 写入超时 30 秒
    .callTimeout(60, TimeUnit.SECONDS)         // 全链路超时(OkHttp 3.13+)
    .build();
参数 OkHttp 版本要求 默认值 行为说明
connectTimeout 所有版本 10 000 ms 域名解析 + TCP 连接建立
readTimeout 所有版本 10 000 ms 从 Socket 读取数据时的最大空闲时间
writeTimeout 所有版本 10 000 ms 向 Socket 写入数据时的最大空闲时间
callTimeout 3.13+ 0(无限制) 整个请求(包括重试、重定向)的总超时

1.3 全局与局部超时优先级

Retrofit 实例通过 .client(OkHttpClient) 设置全局默认超时,但也可通过 @Headers 注解或自定义 Interceptor 对单个接口动态覆盖。优先级从高到低为:

  1. Interceptor 动态修改(最高优先)
  2. @Header 传递自定义超时参数(需要服务端配合或自定义 Interceptor 解析)
  3. OkHttpClient 全局设置(默认基础)
优先级示例:若全局 connectTimeout = 10s,但登录接口因弱网需 30s,可通过 Interceptor 在请求头中添加 X-Connect-Timeout: 30000,再由自定义 Interceptor 读取并修改 Request 的 socket 超时。

1.4 超时参数作用域 SVG 流程图

下图清晰展示了三类超时的触发阶段、参数位置以及全局/局部覆盖关系:

Retrofit 超时参数层级与作用域 OkHttpClient 全局层 Interceptor/注解 局部覆盖层 connectTimeout (默认10s) TCP 连接建立等待上限 readTimeout (默认10s) 读数据包空闲等待上限 writeTimeout (默认10s) 写数据包空闲等待上限 callTimeout (默认0=无限制) 全请求总耗时上限(3.13+) @Headers 动态超时 如 X-Timeout: 30000 自定义 Interceptor 读取请求头并修改 Socket 局部超时优先级 > 全局超时

1.5 常见错误与调试

  • SocketTimeoutException(连接超时):检查网络连通性、DNS 解析、防火墙规则。可在 OkHttp 日志中搜索 connect failed。
  • SocketTimeoutException(读取超时):服务端响应慢或流式接口未及时推数据。建议区分“总响应时间”与“空闲超时”,使用 callTimeout 兜底。
  • InterruptedIOException:写入过程中被中断,常见于上传大文件时用户取消请求。
  • 全局/局部覆盖不生效:确认自定义 Interceptor 的 intercept 方法中调用了 chain.withConnectTimeout(...) 而非 chain.request().newBuilder()(后者仅影响请求头,不修改底层 Socket)。
// 正确的 Interceptor 动态超时写法
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(chain -> {
        Request request = chain.request();
        // 从自定义请求头读取目标超时值
        String timeoutStr = request.header("X-Custom-Timeout");
        int timeoutMs = timeoutStr != null ? Integer.parseInt(timeoutStr) : 10000;
        // 修改链的超时参数(注意不是修改 request)
        return chain.withConnectTimeout(timeoutMs, TimeUnit.MILLISECONDS)
                    .withReadTimeout(timeoutMs, TimeUnit.MILLISECONDS)
                    .proceed(request);
    })
    .build();

1.6 性能与稳定性优化建议

  • 不要使用过短的超时:3 秒以下的连接超时在弱网下极易触发大量重试,反而加剧抖动。
  • 差异化配置:核心接口(如登录、支付)使用稍长超时;非关键日志上报接口可缩短超时,减少对连接池的占用。
  • 配合 Retry 策略:超时触发的异常应在业务层区分是否可重试,避免对幂等性接口重复提交。
  • 监控与日志:通过 OkHttp 的 EventListener 采集连接耗时、请求总耗时,与超时配置阈值对比,及时调整。