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();
提示: 可以同时添加多个 ConverterFactory,Retrofit 按添加顺序依次尝试;例如先添加 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 完整配置流程示意图

Retrofit 基础配置层级与数据流 应用代码 (接口声明) Retrofit 实例 OkHttpClient 超时设置 拦截器 ConverterFactory 关键规则 • BaseUrl 末尾必须加 / • 至少添加一个 ConverterFactory • 测试环境使用独立 OkHttpClient • 超时时间根据网络条件调整 • 应用拦截器优先于网络拦截器 • 日志拦截器 Level=HEADERS 或 BODY • 自定义测试拦截器应存储 trace • 避免在 Worker 中直接打印日志 • 多 BaseUrl 场景用 @Url 参数

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 组合足够,减少反射开销。