OkHttp 是 Square 公司开发的一个高效、功能强大的 HTTP 客户端库,因其简洁的 API、灵活的拦截器链、内置连接池、透明 GZIP 压缩、响应缓存以及对 HTTP/2 和 WebSocket 的支持,已成为 Android 和 Java 应用开发中 事实上的标准 网络库。
一、OkHttp 的核心使用
1. 基本请求流程
// 1. 创建 OkHttpClient 实例 (通常全局共享一个实例)
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS) // 连接超时
.readTimeout(30, TimeUnit.SECONDS) // 读取超时
.writeTimeout(30, TimeUnit.SECONDS) // 写入超时
.cache(new Cache(context.getCacheDir(), 10 * 1024 * 1024)) // 10MB 缓存
.build();
// 2. 构建请求 (Request)
Request request = new Request.Builder()
.url("https://api.example.com/data")
.header("Authorization", "Bearer token123") // 添加请求头
.post(RequestBody.create(MediaType.get("application/json"), jsonData)) // POST 请求体
.build();
// 3. 发起请求 (同步)
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
String responseBody = response.body().string(); // 获取响应体字符串
// 处理 responseBody...
}
// 3. 发起请求 (异步)
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace(); // 处理网络错误
}
@Override
public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody body = response.body()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
String responseData = body.string();
// 切换到主线程更新 UI...
}
}
});
2. 关键组件详解
OkHttpClient: 核心入口点。封装了连接池、线程池、缓存、拦截器链等配置。最佳实践是全局共享一个实例(单例模式),以最大化利用连接池和线程池资源。Request: 封装 HTTP 请求信息(URL、方法、Headers、Body)。Call: 代表一个已准备就绪可以执行的请求。由OkHttpClient.newCall(Request)创建。每个Call实例只能执行一次。Response: 封装 HTTP 响应信息(状态码、Headers、Body)。ResponseBody: 封装响应体数据流。必须关闭(close()或 try-with-resources)以避免资源泄漏。可通过string(),bytes(),charStream(),byteStream(),source()等方法读取内容。
3. 核心功能使用
-
拦截器
Interceptor:- 强大的责任链模式,允许在请求发送前和响应返回后插入自定义逻辑。
- 应用场景:日志记录、请求重试、添加通用 Headers、修改请求/响应体、错误统一处理、加解密、Mock 数据等。
// 日志拦截器示例 public class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); long startTime = System.nanoTime(); Log.d("OkHttp", String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers())); Response response = chain.proceed(request); // 关键:将请求传递给下一个拦截器或网络 long endTime = System.nanoTime(); Log.d("OkHttp", String.format("Received response for %s in %.1fms%n%s", response.request().url(), (endTime - startTime) / 1e6d, response.headers())); return response; } } // 添加到 OkHttpClient OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new LoggingInterceptor()) .build();addInterceptor(Interceptor): 应用拦截器,最先执行,最后获得响应。通常用于与请求/响应内容无关的操作(如日志、添加通用 Header)。addNetworkInterceptor(Interceptor): 网络拦截器,在连接建立后、实际网络传输前后执行。可以访问到承载请求的Connection对象。通常用于与网络传输相关的操作(如重定向、重试)。
-
连接池
ConnectionPool:- OkHttp 默认管理一个连接池(
ConnectionPool),复用 HTTP/1.x 连接和 HTTP/2/3 的多路复用连接,显著减少 TCP 握手和 TLS 握手的开销。 - 默认配置:最大空闲连接数 5,空闲连接存活时间 5 分钟。
OkHttpClient.Builder.connectionPool(new ConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit))可自定义。
- OkHttp 默认管理一个连接池(
-
缓存
Cache:- 遵循 HTTP 缓存语义(
Cache-Control,ETag,Last-Modified等)。 OkHttpClient.Builder.cache(new Cache(directory, maxSize))启用。- 缓存响应可避免不必要的网络请求,提高速度和减少流量消耗。
- 遵循 HTTP 缓存语义(
-
超时控制:
connectTimeout: 建立 TCP 连接或 TLS 握手的超时。readTimeout: 从服务器读取数据的超时(两次数据包到达间隔)。writeTimeout: 向服务器写入数据的超时(两次数据包发送间隔)。callTimeout: 整个请求从开始到完成的超时(包含重定向/重试)。
-
Cookie 管理:
- 通过
OkHttpClient.Builder.cookieJar(CookieJar)接口实现。 - 常用实现库:
PersistentCookieJar(如okhttp3:okhttp-urlconnection或第三方库)。
- 通过
-
认证
Authenticator:- 处理
401 Unauthorized和407 Proxy Authentication Required响应。 - 通过
OkHttpClient.Builder.authenticator(Authenticator)和.proxyAuthenticator(Authenticator)设置。 - 在拦截器中实现认证逻辑(如刷新 Token)也很常见。
- 处理
-
WebSocket:
- 提供
WebSocketAPI 用于双向通信。
Request request = new Request.Builder().url("wss://echo.websocket.org").build(); WebSocketListener listener = new WebSocketListener() { /* 实现回调方法 */ }; WebSocket webSocket = client.newWebSocket(request, listener); webSocket.send("Hello!"); // 发送消息 webSocket.close(1000, "Goodbye!"); // 正常关闭 - 提供
-
HTTPS / SSL Pinning:
- 默认信任系统 CA 证书。
- 使用
OkHttpClient.Builder.sslSocketFactory(SSLSocketFactory, X509TrustManager)自定义信任策略。 - 证书锁定 (Certificate Pinning):只信任特定的证书公钥或哈希,增强安全性,防止中间人攻击。
CertificatePinner pinner = new CertificatePinner.Builder() .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") .build(); OkHttpClient client = new OkHttpClient.Builder() .certificatePinner(pinner) .build();
二、OkHttp 的底层机制实现原理
OkHttp 的卓越性能和高可靠性源于其精心设计的内部架构和核心组件:
1. 责任链模式:拦截器链 (Interceptor.Chain)
- 核心思想:将请求处理过程分解成多个独立的步骤(拦截器),每个步骤只关注自己的职责,并通过链式调用 (
chain.proceed(request)) 将请求依次传递下去,最终到达网络层,再将响应依次返回。 - 主要拦截器 (按添加顺序执行):
- 应用拦截器 (
addInterceptor):用户自定义,最先执行请求处理,最后执行响应处理。通常用于全局逻辑(日志、添加头、修改请求体)。 RetryAndFollowUpInterceptor:处理重定向 (3xx 响应) 和可恢复的失败请求重试(如连接失败、路由失败)。BridgeInterceptor:桥接用户请求和网络请求。添加必要的默认 Headers(如Host,User-Agent,Connection,Content-Type,Content-Length,Accept-Encoding),处理 GZIP 压缩(自动解压Content-Encoding: gzip响应体),处理 Cookie(如果有CookieJar)。CacheInterceptor:处理 HTTP 缓存。根据请求和缓存策略决定是否使用缓存响应。可能发送条件请求 (If-Modified-Since,If-None-Match) 验证缓存有效性。将符合条件的响应写入缓存。ConnectInterceptor:关键环节。负责建立到目标服务器的实际网络连接(TCP 或 TLS)。它会从连接池 (ConnectionPool) 中查找可复用的连接,如果找不到则创建新连接。最终得到一个Exchange对象,它封装了底层的连接和用于读写数据的流。- 网络拦截器 (
addNetworkInterceptor):在连接建立后、实际网络传输前执行。可以访问到承载请求的Connection对象。通常用于网络层监控、修改网络请求。 CallServerInterceptor:最终执行网络 I/O。使用Exchange中的流将请求的 Headers 和 Body 写入网络。读取响应的状态行、Headers 和 Body。这是网络操作实际发生的地方。
- 应用拦截器 (
2. 连接管理 (ConnectionPool, RealConnection)
- 连接复用:核心优化手段。HTTP/1.1 默认开启 Keep-Alive,允许在同一个 TCP 连接上发送多个 HTTP 请求/响应。HTTP/2 和 HTTP/3 (QUIC) 原生支持多路复用 (Multiplexing),在单个连接上并行交错传输多个请求/响应流,极大提高效率。
ConnectionPool:- 管理所有空闲的
RealConnection(物理连接)。 - 使用一个后台清理线程 (
Executor),定期扫描并关闭空闲时间超过keepAliveDuration的连接或超过maxIdleConnections数量的多余连接。 - 使用
Deque<RealConnection>存储空闲连接(按最近使用时间排序)。
- 管理所有空闲的
RealConnection:- 封装底层的
Socket(TCP)或SSLSocket(TLS)连接。 - 记录连接的协议(HTTP/1.1, HTTP/2, QUIC)、路由信息(
Route)、创建时间、最后使用时间。 - 对于 HTTP/2,内部维护一个
Http2Connection对象管理多路复用的流 (Stream)。
- 封装底层的
- 获取连接流程 (
ConnectInterceptor):- 尝试从
ConnectionPool获取一个可复用的连接 (address,route匹配,连接可用且未关闭)。 - 如果找不到,根据
RouteSelector选择的路由 (Route),创建一个新的RealConnection。 - 执行 TCP 连接(
Socket.connect())。 - 如果需要(
https),执行 TLS 握手 (SSLSocket.startHandshake())。 - 如果是 HTTP/2,执行 HTTP/2 连接前言 (
connectionPreface)。 - 将新建立的连接放入连接池。
- 将连接交给
Exchange。
- 尝试从
3. 路由选择 (RouteSelector, Route)
Route: 表示一个具体的网络路径,包含:目标地址 (Address- IP 地址或域名、端口、协议)、使用的代理 (Proxy)、连接使用的具体 IP 地址(通过 DNS 查询获得)。RouteSelector:- 负责收集所有可能的
Route。 - 首先根据
Proxy配置(直接、系统代理、指定代理)确定代理类型。 - 如果需要解析域名(非 SOCKS 代理且未提供 IP),执行 DNS 查询(默认使用
InetAddress.getAllByName(),可通过OkHttpClient.Builder.dns(Dns)自定义)。 - 生成所有可能的
Route组合(代理 + IP 地址)。 - 在连接失败(
IOException)或需要重试时,RouteSelector会尝试下一个可用的Route(连接失败自动切换 IP 的原理)。
- 负责收集所有可能的
4. 请求/响应流管理 (Exchange, ExchangeCodec)
Exchange: 在ConnectInterceptor中创建,绑定到一个特定的RealConnection。封装了一次 HTTP 交互的上下文(请求计数、事件监听)。ExchangeCodec:- 真正负责按照 HTTP 协议规范读写请求和响应的接口。
- 不同协议有不同的实现:
Http1ExchangeCodec: 处理 HTTP/1.1 请求。管理Socket的输入/输出流 (Source/Sink)。Http2ExchangeCodec: 处理 HTTP/2 请求。通过Http2Connection创建新的Stream(Http2Stream),并通过流的Source/Sink读写数据。
CallServerInterceptor通过Exchange获取ExchangeCodec来实际读写网络数据。
5. 异步调度与线程池
Dispatcher:- 管理异步请求的执行和调度。
- 维护三个队列:
readyAsyncCalls: 等待执行的异步请求队列。runningAsyncCalls: 正在执行的异步请求队列(包括正在寻找可用连接的请求)。runningSyncCalls: 正在执行的同步请求队列(较少用,因为同步请求通常由调用者线程管理)。
- 使用线程池 (
ExecutorService) 执行异步请求的网络操作。默认线程池配置根据需求动态调整线程数(类似CachedThreadPool)。 - 策略:
setMaxRequests(int max): 最大并发请求数(默认 64)。setMaxRequestsPerHost(int max): 单个主机最大并发请求数(默认 5)。这对避免对单个服务器造成过大压力很重要。
- 当有请求完成时,
Dispatcher会从readyAsyncCalls中取出等待的请求放入线程池执行。
6. HTTP/2 支持
- 多路复用 (Multiplexing):单个 TCP/TLS 连接上可以同时承载多个双向的、独立的请求/响应流 (
Stream)。消除了 HTTP/1.1 的队头阻塞 (Head-of-Line Blocking - 慢请求阻塞后续请求),极大提高并发性能。 - 二进制分帧层:将 HTTP 消息分解为更小的、独立的帧 (
HEADERS,DATA,PRIORITY,RST_STREAM,SETTINGS,PUSH_PROMISE,PING,GOAWAY),并高效编码传输。帧可以交错发送和接收。 - 头部压缩 (HPACK):使用静态 Huffman 编码和动态表维护,大幅减少冗余 Header 的传输开销。
- 服务器推送 (Server Push):服务器可以主动推送客户端可能需要的资源(如 CSS、JS),减少额外的请求往返。OkHttp 通过
PushObserver处理推送。 - 连接管理:OkHttp 自动在支持 HTTP/2 的服务器上复用连接。连接建立时通过 TLS ALPN (Application-Layer Protocol Negotiation) 或明文连接的前言协商协议。
总结
OkHttp 通过以下机制实现高效可靠:
- 拦截器链:模块化、可扩展的处理流程。
- 连接池:大幅减少 TCP/TLS 握手开销,支持 HTTP/1.1 Keep-Alive 和 HTTP/2/3 多路复用。
- 智能路由:自动 DNS、代理处理、连接失败重试。
- 透明 GZIP 与缓存:节省带宽和时间。
- 高效的异步调度 (
Dispatcher):管理并发请求。 - 对现代协议的支持 (HTTP/2, HTTP/3):提升性能。
- 严谨的超时和重试机制:增强鲁棒性。
理解这些底层原理对于高效使用 OkHttp、诊断网络问题、进行高级定制(如自定义拦截器、连接池调优、DNS 策略)至关重要。OkHttp 的设计充分体现了关注点分离和性能优化的思想。