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 对单个接口动态覆盖。优先级从高到低为:
- Interceptor 动态修改(最高优先)
- @Header 传递自定义超时参数(需要服务端配合或自定义 Interceptor 解析)
- OkHttpClient 全局设置(默认基础)
优先级示例:若全局 connectTimeout = 10s,但登录接口因弱网需 30s,可通过 Interceptor 在请求头中添加
X-Connect-Timeout: 30000,再由自定义 Interceptor 读取并修改 Request 的 socket 超时。
1.4 超时参数作用域 SVG 流程图
下图清晰展示了三类超时的触发阶段、参数位置以及全局/局部覆盖关系:
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采集连接耗时、请求总耗时,与超时配置阈值对比,及时调整。