OkHttp 与 Retrofit 详解

概述

在 Java/Android 开发中,OkHttp 和 Retrofit 是两个最核心的 HTTP 客户端库。它们经常一起使用,但职责不同:

  • OkHttp:底层 HTTP 客户端,负责实际的网络通信
  • Retrofit:REST API 封装层,基于 OkHttp,提供声明式的 API 调用方式

一、HTTP 请求的基本流程

在深入理解这两个库之前,先了解一下 HTTP 请求的基本流程:

客户端                              服务器
   │                                  │
   │  ────── 建立 TCP 连接 ──────→    │
   │                                  │
   │  ────── 发送请求 ─────────→    │
   │      method: POST                │
   │      url: /v1/chat/completions   │
   │      headers: Authorization...    │
   │      body: {"model":"gpt-3.5"...}│
   │                                  │
   │  ←────── 接收响应 ─────────    │
   │      status: 200                 │
   │      headers: Content-Type...    │
   │      body: {"id":"chatcmpl-..."} │
   │                                  │
   │  ────── 关闭连接 ───────────→   │

HTTP 客户端库的作用就是帮你处理这个流程的所有细节。


二、OkHttp 详解

2.1 什么是 OkHttp?

OkHttp 是 Square 公司开源的高性能 HTTP 客户端库,是 Android/Java 生态中最流行的 HTTP 框架。

  • 官网:https://square.github.io/okhttp/
  • Maven 依赖
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version>
</dependency>

2.2 核心概念

类名 作用
OkHttpClient HTTP 客户端实例,管理连接池、超时、拦截器等配置
Request 请求对象,包含 URL、Method、Headers、Body
RequestBody 请求体,用于 POST/PUT 请求
Response 响应对象,包含状态码、Headers、Body
Call 一次请求任务,可以同步执行或异步执行
Interceptor 拦截器,在请求前后进行处理

2.3 基本用法

同步 GET 请求
OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
    .url("https://api.openai.com/v1/models")
    .header("Authorization", "Bearer sk-xxx")
    .build();

Response response = client.newCall(request).execute();
String body = response.body().string();
同步 POST 请求
MediaType JSON = MediaType.parse("application/json");
RequestBody body = RequestBody.create(JSON, "{\"key\":\"value\"}");

Request request = new Request.Builder()
    .url("https://api.openai.com/v1/chat/completions")
    .header("Content-Type", "application/json")
    .header("Authorization", "Bearer sk-xxx")
    .post(body)
    .build();

Response response = client.newCall(request).execute();
异步请求
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // 请求失败
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        String body = response.body().string();
        // 处理响应
    }
});

2.4 拦截器机制(核心扩展点)

拦截器是 OkHttp 最强大的特性,可以在请求发出前和响应返回后进行处理:

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(chain -> {
        // 1. 请求发出前处理
        Request originalRequest = chain.request();
        System.out.println("请求URL: " + originalRequest.url());
        
        // 2. 继续执行请求
        Response response = chain.proceed(originalRequest);
        
        // 3. 响应返回后处理
        System.out.println("响应状态: " + response.code());
        
        return response;
    })
    .build();

拦截器执行顺序

请求
  │
  ▼
┌─────────────────┐
│  应用拦截器 1     │  ← 添加认证、日志等
└────────┬────────┘
         ▼
┌─────────────────┐
│  应用拦截器 2     │
└────────┬────────┘
         ▼
┌─────────────────┐
│  OkHttp 核心     │  ← 连接池、重试、缓存
└────────┬────────┘
         ▼
┌─────────────────┐
│  网络拦截器      │  ← 处理网络层
└────────┬────────┘
         ▼
       网络请求

2.5 常用配置

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)      // 连接超时
    .readTimeout(30, TimeUnit.SECONDS)         // 读取超时
    .writeTimeout(30, TimeUnit.SECONDS)        // 写入超时
    .retryOnConnectionFailure(true)            // 连接失败重试
    .addInterceptor(new HttpLoggingInterceptor())  // 日志拦截器
    .addInterceptor(new AuthInterceptor())     // 认证拦截器
    .build();

2.6 OkHttp 的优势

特性 说明
连接池 复用 TCP 连接,减少握手延迟
GZIP 压缩 自动处理请求体压缩/解压
HTTP 缓存 支持响应缓存,减少重复请求
WebSocket 支持长连接双向通信
SSE 支持 Server-Sent Events(流式响应)
拦截器链 灵活的请求/响应处理机制

三、Retrofit 详解

3.1 什么是 Retrofit?

Retrofit 是 Square 公司开源的 REST 客户端库,基于 OkHttp 构建,专注于简化 RESTful API 调用。

  • 官网:https://square.github.io/retrofit/
  • Maven 依赖
<dependency>
    <groupId>com.squareup.retrofit2</groupId>
    <artifactId>retrofit</artifactId>
    <version>2.9.0</version>
</dependency>

3.2 核心思想

传统方式(手动处理):

// 需要手动构造请求、解析响应
String json = "{\"model\":\"gpt-3.5-turbo\"}";
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
    .url("https://api.openai.com/v1/chat/completions")
    .post(body)
    .build();
Response response = client.newCall(request).execute();
ChatResponse resp = objectMapper.readValue(response.body().string(), ChatResponse.class);

使用 Retrofit(声明式):

// 1. 定义接口
public interface OpenAiApi {
    @POST("v1/chat/completions")
    Call<ChatResponse> completions(@Body ChatRequest request);
}

// 2. 创建实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.openai.com/")
    .build();
OpenAiApi api = retrofit.create(OpenAiApi.class);

// 3. 调用就像调用本地方法
ChatResponse resp = api.completions(request).execute().body();

3.3 工作原理(动态代理)

Retrofit 使用 JDK 动态代理 实现接口方法到 HTTP 请求的转换:

// 当你调用 api.completions(request) 时
ChatResponse resp = api.completions(request);

// 内部执行的逻辑(简化版)
public Object invoke(Object proxy, Method method, Object[] args) {
    // 1. 读取注解信息
    POST annotation = method.getAnnotation(POST.class);
    String path = annotation.value();  // "v1/chat/completions"
    
    // 2. 处理参数
    ChatRequest request = (ChatRequest) args[0];
    
    // 3. 构造请求
    Request okHttpRequest = new Request.Builder()
        .url(baseUrl + path)
        .post(serializeToJson(request))
        .build();
    
    // 4. 发送请求并解析
    Response response = okHttpClient.newCall(okHttpRequest).execute();
    return deserializeFromJson(response.body().string());
}

执行流程图

chatApi.completions(request)
          │
          ▼
  ┌───────────────────┐
  │  Retrofit 代理     │  ← 拦截方法调用
  └─────────┬─────────┘
            │
            ▼
  ┌───────────────────┐
  │ 读取接口注解       │  @POST、@Body 等
  └─────────┬─────────┘
            │
            ▼
  ┌───────────────────┐
  │ 序列化请求对象     │  Jackson → JSON
  └─────────┬─────────┘
            │
            ▼
  ┌───────────────────┐
  │ 发送 OkHttp 请求   │
  └─────────┬─────────┘
            │
            ▼
  ┌───────────────────┐
  │ 反序列化响应      │  JSON → Java 对象
  └─────────┬─────────┘
            │
            ▼
        返回结果

3.4 常用注解

注解 作用 示例
@GET GET 请求 @GET("v1/models")
@POST POST 请求 @POST("v1/chat/completions")
@PUT PUT 请求 @PUT("v1/models/{id}")
@DELETE DELETE 请求 @DELETE("v1/models/{id}")
@Body 请求体 @Body ChatRequest request
@Path URL 路径参数 @Path("id") String id
@Query URL 查询参数 @Query("limit") int limit
@Header 请求头 @Header("Authorization") String auth
@Headers 静态请求头 @Headers("Content-Type: application/json")
@FormUrlEncoded 表单编码
@Multipart 多部分请求

3.5 转换器(Converter)

Retrofit 需要转换器来处理 JSON 和 Java 对象之间的转换:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.openai.com/")
    .addConverterFactory(JacksonConverterFactory.create())    // Jackson
    // .addConverterFactory(GsonConverterFactory.create())   // Gson
    // .addConverterFactory(MoshiConverterFactory.create())  // Moshi
    .build();

3.6 调用适配器(Call Adapter)

调用适配器决定方法返回值的类型:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.openai.com/")
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())  // RxJava
    // .addCallAdapterFactory(CoroutineCallAdapterFactory.create()) // Kotlin Coroutine
    .build();

// 使用 RxJava
public interface OpenAiApi {
    @POST("v1/chat/completions")
    Single<ChatResponse> completions(@Body ChatRequest request);
}

四、OkHttp 与 Retrofit 的配合

4.1 基本配置

// 1. 先配置 OkHttp
OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .addInterceptor(new AuthInterceptor())
    .addInterceptor(new HttpLoggingInterceptor())
    .build();

// 2. 再配置 Retrofit
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.openai.com/")
    .client(okHttpClient)                                    // 使用 OkHttp
    .addConverterFactory(JacksonConverterFactory.create())   // JSON 转换
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // RxJava
    .build();

// 3. 创建 API 实例
OpenAiApi api = retrofit.create(OpenAiApi.class);

4.2 在 SDK 中的应用

在 ChatGPT SDK 中的实际应用示例:

// DefaultOpenAiSessionFactory.java
@Override
public OpenAiSession openSession() {
    // 1. 创建 OkHttpClient
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(new HttpLoggingInterceptor())
        .addInterceptor(new OpenAiInterceptor(apiKey, authToken))
        .connectTimeout(450, TimeUnit.SECONDS)
        .build();

    // 2. 创建 Retrofit
    IOpenAiApi openAiApi = new Retrofit.Builder()
        .baseUrl(configuration.getApiHost())
        .client(okHttpClient)
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(JacksonConverterFactory.create())
        .build().create(IOpenAiApi.class);

    // 3. 返回会话
    return new DefaultOpenAiSession(configuration);
}

五、对比总结

5.1 核心差异

对比项 OkHttp Retrofit
定位 底层 HTTP 客户端 REST API 封装层
职责 处理实际网络通信 简化 API 调用
用法 命令式(手动构造请求) 声明式(接口 + 注解)
灵活性 高(底层控制) 低(受限于注解)
学习曲线 较简单 较陡峭(注解较多)
适用场景 需要精细控制 HTTP 细节 标准 REST API 调用

5.2 二者关系图

┌─────────────────────────────────────────────────────────────┐
│                    架构层次                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   应用层                                                     │
│   └── API 接口定义(Retrofit)                                │
│                                                             │
│   封装层                                                     │
│   └── Retrofit(注解处理、序列化、动态代理)                   │
│                                                             │
│   传输层                                                     │
│   └── OkHttp(连接池、拦截器、HTTP 协议)                     │
│                                                             │
│   网络层                                                     │
│   └── TCP/IP                                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.3 使用建议

场景 推荐方案
简单的 HTTP 请求 直接使用 OkHttp
复杂的 REST API Retrofit + OkHttp
需要流式响应(SSE) OkHttp 原生支持
需要自定义认证逻辑 OkHttp 拦截器
需要优雅的 API 设计 Retrofit

六、常见问题

6.1 为什么要用拦截器?

  • 统一处理:认证、日志、重试等逻辑集中处理
  • 解耦:业务代码与 HTTP 细节分离
  • 可复用:拦截器可以在多个请求中复用

6.2 Retrofit 返回的 Call 是什么?

Call<T> 是 Retrofit 的请求封装类:

  • execute():同步执行
  • enqueue(Callback):异步执行
  • cancel():取消请求
  • clone():克隆请求

6.3 如何处理错误?

try {
    Response<ChatResponse> response = api.completions(request).execute();
    if (response.isSuccessful()) {
        ChatResponse body = response.body();
        // 成功处理
    } else {
        String errorBody = response.errorBody().string();
        // 错误处理
    }
} catch (IOException e) {
    // 网络错误
}

6.4 为什么需要转换器?

Retrofit 本身不处理 JSON 序列化,需要依赖转换器:

  • Jackson
  • Gson
  • Moshi

七、总结

定位 核心价值
OkHttp 底层 HTTP 客户端 高性能、可扩展、支持流式响应
Retrofit REST API 封装层 声明式 API、自动序列化、代码整洁

最佳实践

  1. 使用 Retrofit 定义 API 接口
  2. 使用 OkHttp 配置底层通信(超时、拦截器等)
  3. 使用 Jackson/Gson 处理 JSON 转换
  4. 使用 RxJava/Coroutine 处理异步

比喻

  • OkHttp = 快递员,负责高效地把包裹送到目的地
  • Retrofit = 快递公司的 App,让你用最简洁的方式下单
  • 转换器 = 包裹打包服务,自动把物品打包成标准格式

注意:retrofit不支持sse。

SSE 是什么?
- 流式响应,服务器一段一段推送数据
- 像看视频,边下边播
- 需要持续监听数据流

Retrofit 的定位:
- 标准的 Request/Response
- 一次请求,一次响应
- 不适合流式场景

OkHttp SSE:
- OkHttp 原生支持 SSE
- EventSource 可以持续监听
- 适合流式响应

流式请求的典型使用场景

  1. 提升用户体验 - 像官网一样逐字显示
    当 AI 生成较长内容时(如写代码、写文章),流式请求可以让内容 逐字逐句地显示 ,而不是等全部生成完才一次性显示。
  2. 处理长响应内容
    当 AI 回复内容很长时,流式请求可以:
  • 避免等待时间过长导致用户焦虑
  • 可以在收到部分内容时就开始处理
  • 减少内存占用(不需要一次性加载完整响应)
  1. 实时交互场景
  • 聊天机器人 :边生成边显示,模拟真人打字效果
  • 代码助手 :生成代码时可以逐行显示
  • 内容创作 :写文章、故事时实时预览
  1. 可中断的请求
    流式请求可以随时取消(通过 EventSource.cancel() ),如果用户发现 AI 跑题了,可以立即停止。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐