2. Retrofit 基础配置:BaseUrl、ConverterFactory、OkHttpClient、超时与拦截器
Retrofit 是一个类型安全的 HTTP 客户端,其核心工作是将 Java 接口方法声明转换为 HTTP 请求。所有请求行为均通过一套可组合的配置层控制,包括 BaseUrl、ConverterFactory、OkHttpClient 以及其内部的超时与拦截器。本章从原理与实现流程出发,逐步拆解每个配置项的作用、典型参数及常见陷阱。
2.1 配置项总览
Retrofit 使用建造者模式构建实例。一次完整的配置涉及 4 层结构:
| 层级 | 组件 | 职责 | 典型实现 |
|---|---|---|---|
| 1 | BaseUrl | 定义所有相对请求的根地址 | https://api.example.com/v2/ |
| 2 | ConverterFactory | 序列化/反序列化请求与响应体 | GsonConverterFactory, MoshiConverterFactory |
| 3 | OkHttpClient | 执行实际网络请求,管理连接池、缓存、拦截器 | OkHttpClient.Builder |
| 4 | 超时 & 拦截器 | 控制请求等待时间、日志、重试、认证 | connectTimeout, HttpLoggingInterceptor |
2.2 BaseUrl —— 必须遵守的 URL 格式
原理:BaseUrl 是 Retrofit 构建完整请求地址的锚点。所有接口方法中声明的 @GET("users") 等相对路径会拼接在 BaseUrl 之后。
实现规则:
- BaseUrl 必须以
/结尾(否则 Retrofit 会强制要求接口方法路径以/开头,容易造成混淆)。 - 建议使用 HTTPS 而非 HTTP,除非是测试环境。
- BaseUrl 在创建后不可变,若需多环境切换请创建多个 Retrofit 实例或使用
@Url动态替换。
// 正确示例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
// 错误示例:末尾无斜杠,接口方法路径必须以 / 开头
// .baseUrl("https://api.github.com")
2.3 ConverterFactory —— 让 JSON 与 POJO 自由转换
原理:Retrofit 只负责构建请求、解析响应元数据,但 不处理 请求体/响应体的序列化。ConverterFactory 通过 SPI 机制将 okhttp3.RequestBody 和 okhttp3.ResponseBody 转换为 Java/Kotlin 对象。
常用工厂:
| 工厂 | 底层库 | 特点 |
|---|---|---|
GsonConverterFactory |
Gson | 兼容性好,但序列化时默认使用反射 |
MoshiConverterFactory |
Moshi | 更好的性能与 Kotlin 支持 |
JacksonConverterFactory |
Jackson | 功能丰富,注解强大 |
ScalarsConverterFactory |
无 | 处理原始类型(String/Integer)不依赖 JSON |
配置示例:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ScalarsConverterFactory,再添加 GsonConverterFactory,可同时支持 String 和 JSON 响应。
2.4 OkHttpClient —— 网络层的“引擎”
原理:Retrofit 本身不发送 HTTP 请求,它委托给 OkHttpClient 执行。通过自定义 OkHttpClient,可以精细控制 连接池、缓存、DNS、代理 以及下一小节要讲的 超时和拦截器。
实现流程:创建一个共享的 OkHttpClient 单例,再传递给 Retrofit。
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(5, 30, TimeUnit.SECONDS))
.cache(new Cache(cacheDir, 10 * 1024 * 1024)) // 10 MB 缓存
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
调试要点:在 Worker 测试中,应确保 OkHttpClient 实例不与测试代码共享生产环境的连接池,避免请求干扰。使用 new OkHttpClient.Builder() 新建一个专用于测试的实例。
2.5 超时配置
原理:OkHttp 支持三个维度的超时,防止请求被无限制阻塞。
- connectTimeout:建立 TCP 连接的最大等待时间,默认 10 秒。
- readTimeout:读取响应数据的最大等待时间,默认 10 秒。
- writeTimeout:写入请求体的最大等待时间,默认 10 秒。
典型配置:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.build();
connectTimeout 而忘记调整 readTimeout,导致响应数据稍大时抛出 SocketTimeoutException。在 Worker 测试中,建议将读写超时设为 30 秒以上,避免网络抖动导致测试失败。
2.6 拦截器 —— 请求/响应的“中间件”
原理:拦截器是 OkHttp 提供的责任链模式组件,可在请求发送前、收到响应后插入自定义逻辑。Retrofit 没有独立的拦截器机制,所有拦截器统一通过 OkHttpClient 注册。
两类拦截器:
| 类型 | 添加方式 | 执行时机 | 常见用途 |
|---|---|---|---|
| 应用拦截器 | addInterceptor() |
在重试和重定向之前 | 添加公共 Header、日志打印 |
| 网络拦截器 | addNetworkInterceptor() |
在重试和重定向之后 | 监控网络耗时、修改响应缓存 |
代码示例——日志拦截器(常用于调试):
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY); // 打印完整请求/响应
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
在 Worker 测试环境中的特殊处理:测试时不应依赖 Logcat 输出(因为 Worker 可能运行在后台进程),建议将拦截逻辑改为将请求/响应数据保存到 StringBuilder 中,供断言检查。
// 自定义测试拦截器
class TestInterceptor implements Interceptor {
private final StringBuilder trace = new StringBuilder();
@Override
public Response intercept(Chain chain) {
Request request = chain.request();
trace.append("Request: ").append(request.method()).append(" ")
.append(request.url()).append("\n");
Response response = chain.proceed(request);
trace.append("Response: ").append(response.code()).append("\n");
return response;
}
public String getTrace() { return trace.toString(); }
}
2.7 完整配置流程示意图
2.8 常见错误与排查
| 错误现象 | 根因 | 解决方案 |
|---|---|---|
IllegalArgumentException: baseUrl must have a trailing / |
BaseUrl 末尾缺少斜杠 | 补上 / |
SocketTimeoutException: connect timed out |
连接超时过短,或网络不可达 | 增加 connectTimeout;检查模拟器/WiFi |
响应体解析为 null |
未添加对应的 ConverterFactory 或响应体为空 | 确认添加了 GsonConverterFactory 等;检查接口方法返回值类型 |
| 拦截器不生效 | 拦截器注册在错误的 OkHttpClient 实例上 | 确保将构建的 OkHttpClient 传入 retrofit.client() |
2.9 Worker 测试中的部署建议
在 Worker 测试中,Retrofit 配置应遵循以下原则:
- 隔离性:每次测试都创建新的
Retrofit实例,或者使用依赖注入将配置与生产环境完全隔开。 - 确定性:超时设置为固定的大数值(如 60 秒),避免网络条件波动导致测试 flaky。
- 可观测性:注册自定义拦截器,将请求/响应详情存入
TestObserver,以便断言验证。 - 无副作用:禁止在生产 OkHttpClient 上注册日志拦截器(可能导致测试日志暴增),始终在测试专用的 Builder 中配置。
2.10 性能与稳定性优化
- 连接池复用:为所有测试共享同一个
OkHttpClient(非生产 Client),避免频繁创建连接。 - 禁用重试:测试环境中应禁用 OkHttp 自动重试(
retryOnConnectionFailure(false)),以免掩盖真正的网络故障。 - 使用轻量级 Converter:测试无需处理复杂的序列化场景,
ScalarsConverterFactory+GsonConverterFactory组合足够,减少反射开销。