第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 请求流程概览 ① App 调用 getUser(42) ② Retrofit 动态代理 构建 OkHttp Request ③ OkHttp RealCall 入队/执行 ④ 拦截器链 应用拦截器 → 网络拦截器 → 桥接拦截器 ⑤ 网络 I/O HTTP/1.1 或 HTTP/2 ⑥ Response → 应用层回调 关键点说明 • 动态代理将接口方法调用转为 OkHttp Request • 拦截器链按逆序执行,提供请求/响应拦截点 • 网络 I/O 由 OkHttp 连接池复用 TCP 连接
流程要点:
  1. 应用层调用接口方法时,Retrofit 的动态代理生成 OkHttp Request。
  2. 将 Request 封装为 OkHttp RealCall,并执行 execute()(同步)或 enqueue()(异步)。
  3. OkHttp 按顺序执行拦截器链:应用层添加的拦截器 → 重试/重定向拦截器 → 桥接拦截器 → 网络拦截器 → 网络 I/O。
  4. 响应经反向拦截器链处理后,以 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 性能与稳定性优化

  1. 连接池配置: 对于高并发应用,增大 OkHttp 连接池大小并设置空闲保活时间,减少三次握手开销:
    ConnectionPool pool = new ConnectionPool(10, 5, TimeUnit.MINUTES);
    OkHttpClient client = new OkHttpClient.Builder()
            .connectionPool(pool)
            .build();
  2. 超时合理设置: 根据业务响应时效调整 connectTimeout(默认 10s,可降至 5s 内)、readTimeout(大文件下载需加大)。
  3. 使用 LruCache 缓存接口实例: 避免重复创建 Retrofit 对象,同一 Base URL 应复用 builder:
    private static final Map<String, Retrofit> retrofitCache = new LruCache<>(4);
  4. 添加重试机制: 非幂等请求避免自动重试,幂等请求可在应用层或拦截器内加入指数退避。
调试建议:在开发环境添加 HttpLoggingInterceptor 并设置 Level.BODY,查看完整请求 URL、Header 和 Body。生产环境建议关闭 BODY 日志以防敏感信息泄露。