OkHttp 网络请求类型全解析
简介
OkHttp 是一个高效的 HTTP 客户端,支持多种网络通信协议和请求类型。它由 Square 公司开发,被广泛应用于 Android 和 Java 应用程序中。OkHttp 的设计理念是简单、高效、可扩展,能够自动处理常见的网络问题,如连接池、GZIP 压缩、响应缓存等。
本文档详细介绍 OkHttp 支持的各种请求类型,包括代码示例和使用场景对比,帮助开发者选择最适合自己应用场景的通信方式。
目录
1. 标准 HTTP/HTTPS 请求
特点
- 支持 HTTP/1.1 和 HTTP/2 协议
- 请求-响应模式
- 支持 GET, POST, PUT, DELETE, HEAD, PATCH 等方法
- 短连接或长连接(Keep-Alive)
- 支持同步和异步调用
- 支持拦截器链
- 自动处理 Cookie
- 支持响应缓存
适用场景
- REST API 调用
- 资源下载/上传
- 表单提交
- 一般的客户端-服务器通信
代码示例
同步请求
val client = OkHttpClient()
// 创建请求
val request = Request.Builder()
.url("https://api.example.com/data")
.header("User-Agent", "OkHttp Example")
.build()
// 执行同步调用
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
// 处理响应
val responseBody = response.body?.string()
println(responseBody)
}
异步请求
val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.example.com/data")
.build()
// 执行异步调用
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
val responseBody = response.body?.string()
println(responseBody)
}
}
})
POST 请求
val client = OkHttpClient()
val json = """
{
"name": "John Doe",
"email": "john@example.com"
}
""".trimIndent()
val requestBody = json.toRequestBody("application/json; charset=utf-8".toMediaType())
val request = Request.Builder()
.url("https://api.example.com/users")
.post(requestBody)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
println(response.body?.string())
}
文件上传
val client = OkHttpClient()
val file = File("document.pdf")
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"file",
file.name,
file.asRequestBody("application/pdf".toMediaType())
)
.addFormDataPart("description", "PDF document")
.build()
val request = Request.Builder()
.url("https://api.example.com/upload")
.post(requestBody)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
println(response.body?.string())
}
2. WebSocket 请求
特点
- 全双工通信
- 基于 TCP 的持久连接
- 低延迟
- 支持二进制和文本数据
- 客户端和服务器都可以主动发送消息
- 适合实时应用
适用场景
- 实时聊天应用
- 在线游戏
- 协作编辑工具
- 实时数据仪表板
- 需要频繁双向通信的应用
代码示例
val client = OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 需要禁用读取超时
.build()
val request = Request.Builder()
.url("ws://echo.websocket.org")
.build()
val listener = object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
println("WebSocket connection opened")
webSocket.send("Hello, WebSocket!")
}
override fun onMessage(webSocket: WebSocket, text: String) {
println("Received text message: $text")
}
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
println("Received binary message: ${bytes.hex()}")
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
println("WebSocket closing: $code / $reason")
webSocket.close(1000, null)
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
println("WebSocket closed: $code / $reason")
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
println("WebSocket failure: ${t.message}")
}
}
val webSocket = client.newWebSocket(request, listener)
// 发送消息
webSocket.send("Another message")
// 发送二进制消息
val byteString = "Binary data".encodeUtf8()
webSocket.send(byteString)
// 关闭 WebSocket
// webSocket.close(1000, "Goodbye, WebSocket!")
3. Server-Sent Events (SSE) 请求
特点
- 单向通信(服务器到客户端)
- 基于 HTTP 的长连接
- 自动重连机制
- 纯文本格式
- 支持事件类型和 ID
- 适合服务器推送场景
适用场景
- 实时通知
- 股票行情更新
- 社交媒体动态
- 日志流
- 进度更新
代码示例
val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(90, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url("https://example.com/sse-endpoint")
.header("Accept", "text/event-stream")
.build()
val factory = EventSources.createFactory(client)
val listener = object : EventSourceListener() {
override fun onOpen(eventSource: EventSource, response: Response) {
println("SSE connection opened: ${response.code}")
}
override fun onEvent(
eventSource: EventSource,
id: String?,
type: String?,
data: String
) {
println("Event received:")
println(" id: $id")
println(" type: $type")
println(" data: $data")
// 处理不同类型的事件
when (type) {
"update" -> handleUpdate(data)
"notification" -> showNotification(data)
null -> handleDefaultEvent(data)
}
}
override fun onClosed(eventSource: EventSource) {
println("SSE connection closed normally")
}
override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {
println("SSE connection failed: ${t?.message}")
response?.let { println("Response code: ${it.code}") }
}
}
val eventSource = factory.newEventSource(request, listener)
// 在适当的时候取消连接
// eventSource.cancel()
4. HTTP/2 请求
特点
- 多路复用(单个连接上的并行请求)
- 头部压缩
- 服务器推送
- 二进制格式
- 流优先级
- 与 HTTP/1.1 向后兼容
适用场景
- 需要高性能的 Web 应用
- 移动应用(减少电池消耗)
- 资源密集型网站
- 需要并行加载多个资源的场景
代码示例
// OkHttp 默认支持 HTTP/2,但可以明确指定
val client = OkHttpClient.Builder()
.protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))
.build()
val request = Request.Builder()
.url("https://example.com")
.build()
client.newCall(request).execute().use { response ->
println("Protocol: ${response.protocol}")
if (!response.isSuccessful) throw IOException("Unexpected code $response")
// 处理响应
val responseBody = response.body?.string()
println(responseBody)
}
查看 HTTP/2 推送的资源
val client = OkHttpClient.Builder()
.protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))
.build()
val request = Request.Builder()
.url("https://example.com")
.build()
client.newCall(request).execute().use { response ->
// 检查是否有服务器推送的资源
val pushPromises = response.exchange?.connection?.protocol == Protocol.HTTP_2
if (pushPromises) {
// 注意:这只是概念性代码,OkHttp 目前没有直接暴露推送资源的 API
// 实际上,OkHttp 会自动缓存推送的资源,并在后续请求中使用
println("This connection supports HTTP/2 server push")
}
println(response.body?.string())
}
5. gRPC 请求
特点
- 基于 HTTP/2
- 使用 Protocol Buffers 序列化
- 支持流式 RPC
- 强类型接口
- 代码生成
- 支持多种语言
适用场景
- 微服务架构
- 高性能 API
- 需要严格接口定义的系统
- 多语言环境
代码示例
首先,在 build.gradle 文件中添加必要的依赖:
dependencies {
// gRPC 核心依赖
implementation 'io.grpc:grpc-okhttp:1.53.0'
implementation 'io.grpc:grpc-protobuf:1.53.0'
implementation 'io.grpc:grpc-stub:1.53.0'
// 如果在 Android 上使用
implementation 'io.grpc:grpc-android:1.53.0'
}
定义 Protocol Buffers 文件 (greeter.proto):
syntax = "proto3";
option java_package = "com.example.grpc";
option java_multiple_files = true;
package greeter;
// 定义服务
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloStream (HelloRequest) returns (stream HelloReply) {}
}
// 请求消息
message HelloRequest {
string name = 1;
}
// 响应消息
message HelloReply {
string message = 1;
}
使用 OkHttp 作为 gRPC 传输层的客户端代码:
import io.grpc.ManagedChannel;
import io.grpc.okhttp.OkHttpChannelBuilder;
import io.grpc.stub.StreamObserver;
import com.example.grpc.GreeterGrpc;
import com.example.grpc.HelloRequest;
import com.example.grpc.HelloReply;
import java.util.concurrent.TimeUnit;
public class GrpcClient {
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
private final GreeterGrpc.GreeterStub asyncStub;
public GrpcClient(String host, int port) {
// 使用 OkHttpChannelBuilder 创建 gRPC 通道
channel = OkHttpChannelBuilder.forAddress(host, port)
.usePlaintext() // 用于非 TLS 连接,生产环境应使用 TLS
.keepAliveTime(30, TimeUnit.SECONDS)
.keepAliveTimeout(10, TimeUnit.SECONDS)
.build();
// 创建存根
blockingStub = GreeterGrpc.newBlockingStub(channel);
asyncStub = GreeterGrpc.newStub(channel);
}
// 同步调用示例
public String sayHello(String name) {
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response = blockingStub.sayHello(request);
return response.getMessage();
}
// 流式调用示例
public void sayHelloStream(String name, final StreamObserver<String> responseObserver) {
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
asyncStub.sayHelloStream(request, new StreamObserver<HelloReply>() {
@Override
public void onNext(HelloReply reply) {
responseObserver.onNext(reply.getMessage());
}
@Override
public void onError(Throwable t) {
responseObserver.onError(t);
}
@Override
public void onCompleted() {
responseObserver.onCompleted();
}
});
}
// 关闭通道
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
// 使用示例
public static void main(String[] args) {
GrpcClient client = new GrpcClient("localhost", 50051);
try {
// 同步调用
String response = client.sayHello("World");
System.out.println("Response: " + response);
// 流式调用
client.sayHelloStream("Stream World", new StreamObserver<String>() {
@Override
public void onNext(String message) {
System.out.println("Stream response: " + message);
}
@Override
public void onError(Throwable t) {
System.err.println("Stream error: " + t.getMessage());
}
@Override
public void onCompleted() {
System.out.println("Stream completed");
}
});
// 等待流式响应
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
client.shutdown();
}
}
}
6. 请求类型对比
下表对比了 OkHttp 支持的各种请求类型的关键特性:
| 特性 | HTTP | WebSocket | SSE | HTTP/2 | gRPC |
|---|---|---|---|---|---|
| 通信方向 | 双向(请求-响应) | 全双工(双向同时) | 单向(服务器到客户端) | 双向 | 双向(支持流式) |
| 连接类型 | 短连接/长连接 | 持久连接 | 长连接 | 持久连接 | 持久连接 |
| 实时性 | 低 | 高 | 中 | 中 | 高 |
| 数据格式 | 任意 | 文本/二进制 | 文本 | 二进制 | Protocol Buffers |
| 自动重连 | 否 | 需自行实现 | 是 | 否 | 可配置 |
| 头部压缩 | HTTP/1.1 无,HTTP/2 有 | 无 | 无 | 有 | 有 |
| 多路复用 | HTTP/1.1 无,HTTP/2 有 | 不适用 | 不适用 | 有 | 有 |
| 资源消耗 | 低 | 中 | 低 | 低 | 中 |
| 浏览器支持 | 全部 | 全部现代浏览器 | 大多数现代浏览器 | 大多数现代浏览器 | 需要特殊库 |
| 开发复杂度 | 低 | 中 | 低 | 低 | 高 |
| 适用场景 | 通用 API 调用 | 实时双向通信 | 服务器推送更新 | 高性能 API | 微服务通信 |
7. 如何选择合适的请求类型
选择合适的请求类型取决于你的应用需求。以下是一些选择指南:
使用标准 HTTP/HTTPS 请求,当你需要:
- 简单的请求-响应交互
- 与 RESTful API 通信
- 上传或下载文件
- 最广泛的兼容性
- 简单的实现和维护
使用 WebSocket,当你需要:
- 低延迟的双向通信
- 客户端和服务器都能主动发送消息
- 实时应用,如聊天、游戏或协作工具
- 减少连接建立的开销
- 支持二进制数据传输
使用 SSE,当你需要:
- 服务器向客户端推送更新
- 自动重连机制
- 比 WebSocket 更简单的实现
- 与现有 HTTP 基础设施更好的兼容性
- 只需要文本数据传输
使用 HTTP/2,当你需要:
- 提高性能和减少延迟
- 在单个连接上并行发送多个请求
- 减少头部数据的传输量
- 服务器推送资源
- 与 HTTP/1.1 向后兼容
使用 gRPC,当你需要:
- 高性能的 RPC 通信
- 强类型接口和代码生成
- 支持流式数据传输
- 多语言支持
- 适合微服务架构
实际应用中的混合使用
在实际应用中,你可能需要混合使用多种请求类型:
- 使用标准 HTTP 请求处理一般的 API 调用和资源获取
- 使用 WebSocket 实现聊天功能或实时协作
- 使用 SSE 推送通知或更新
- 使用 HTTP/2 提高整体性能
- 使用 gRPC 进行微服务间的高效通信
OkHttp 的灵活性使其成为处理各种网络通信需求的理想选择。通过了解每种请求类型的特点和适用场景,你可以为你的应用选择最合适的通信方式,提高性能和用户体验。