第2章 Retrofit网络请求基础:接口定义、OkHttp集成、请求流程、响应拦截
本章聚焦于 Retrofit 网络请求的核心环节:如何通过声明式接口定义网络请求、如何集成 OkHttp 作为底层 HTTP 引擎、一次完整请求从发起至响应的执行流程,以及如何借助 OkHttp 的拦截器机制实现对请求/响应的统一拦截与处理。这些基础是后续实现离线缓存机制的必备前提。
2.1 接口定义:注解驱动的声明式 API
Retrofit 将 HTTP API 声明为 Java 或 Kotlin 接口,每个方法对应一个端点的请求。通过方法注解(如 @GET、@POST、@PUT、@DELETE)和参数注解(如 @Path、@Query、@Body)描述请求方式、路径、参数等。以下是一个典型接口定义:
public interface ApiService {
@GET("users/{id}")
Call<User> getUser(@Path("id") long userId);
@POST("users")
Call<User> createUser(@Body User user);
@GET("users")
Call<List<User>> listUsers(@Query("page") int page,
@Query("size") @Default("20") int size);
}
提示:注解中的字符串会自动拼接在 Base URL 之后;动态路径段使用花括号
{id} 标记,并通过 @Path 填充值。2.2 OkHttp 集成:作为 HTTP 层引擎
Retrofit 本身不实现网络通信,它默认使用 OkHttp 作为底层 HTTP 客户端。创建 Retrofit 实例时需要传入自行构造的 OkHttpClient,以便集中配置超时、连接池、拦截器、Cookie 管理等。典型集成代码:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService apiService = retrofit.create(ApiService.class);
注意:不配置
OkHttpClient 时,Retrofit 会使用默认的 OkHttp 实例,该实例的连接池较小、无超时,在高并发场景下容易导致连接耗尽或长时间阻塞。务必显式创建并配置。2.3 请求流程解析
从应用层调用接口方法到服务器响应返回,内部经历了 Retrofit 动态代理、OkHttp Call、拦截器链、网络 I/O 等环节。下图以用户查询 getUser(42) 为例展示完整流程:
流程要点:
- 应用层调用接口方法时,Retrofit 的动态代理生成
OkHttp Request。 - 将 Request 封装为
OkHttp RealCall,并执行execute()(同步)或enqueue()(异步)。 - OkHttp 按顺序执行拦截器链:应用层添加的拦截器 → 重试/重定向拦截器 → 桥接拦截器 → 网络拦截器 → 网络 I/O。
- 响应经反向拦截器链处理后,以
Response对象返回给应用层。
2.4 响应拦截:OkHttp Interceptor 实战
拦截器是 OkHttp 提供的扩展点,可在请求发送前或响应返回后插入自定义逻辑。按添加方式分为两类:
| 拦截器类型 | 添加方法 | 触发时机 | 典型用途 |
|---|---|---|---|
| 应用拦截器 | addInterceptor() | 请求准备后,重试/重定向前 | 日志记录、添加固定 Header、请求签名 |
| 网络拦截器 | addNetworkInterceptor() | 每次网络 I/O 前后(含重试) | 网络层面监控、缓存控制、重定向跟踪 |
以下是一个捕获响应状态码并触发自动重试的拦截器示例:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(chain -> {
Request request = chain.request();
Response response = chain.proceed(request);
if (response.code() >= 500 && response.code() < 600) {
// 服务端错误,尝试重试(最多 3 次)
for (int i = 0; i < 2; i++) {
response.close();
response = chain.proceed(request);
if (response.isSuccessful()) break;
}
}
return response;
})
.build();
陷阱:拦截器内直接修改
Response 的 body 内容可能导致流关闭异常。如需解码/压缩 body,应使用 ResponseBody 的 bytes() 或 string() 后重建 Response 并正确设置 body 类型。同时注意拦截器内不要阻塞线程太久(异步场景需切换线程)。2.5 常见错误与排查
- 超时异常:
SocketTimeoutException— 检查OkHttpClient的超时配置是否过短,或服务端响应慢。 - 非法 URL:
MalformedUrlException— 确保@GET等注解中的路径不以/开头或与 Base URL 拼接出错。 - Converter 未注册:
IllegalArgumentException: Unable to create converter for ...— 检查addConverterFactory是否包含所需类型。 - 拦截器导致无限重试: 循环中未检查响应状态或未限制最大重试次数,导致堆栈溢出。
2.6 性能与稳定性优化
- 连接池配置: 对于高并发应用,增大 OkHttp 连接池大小并设置空闲保活时间,减少三次握手开销:
ConnectionPool pool = new ConnectionPool(10, 5, TimeUnit.MINUTES); OkHttpClient client = new OkHttpClient.Builder() .connectionPool(pool) .build(); - 超时合理设置: 根据业务响应时效调整
connectTimeout(默认 10s,可降至 5s 内)、readTimeout(大文件下载需加大)。 - 使用 LruCache 缓存接口实例: 避免重复创建
Retrofit对象,同一 Base URL 应复用 builder:private static final Map<String, Retrofit> retrofitCache = new LruCache<>(4); - 添加重试机制: 非幂等请求避免自动重试,幂等请求可在应用层或拦截器内加入指数退避。
调试建议:在开发环境添加
HttpLoggingInterceptor 并设置 Level.BODY,查看完整请求 URL、Header 和 Body。生产环境建议关闭 BODY 日志以防敏感信息泄露。