OkHttp 网络请求执行流程分析
源码参考:square/okhttp (GitHub)
一、整体结构
OkHttpClient
│
└── newCall(Request) ──→ RealCall
│
├── execute() 同步执行
└── enqueue() 异步执行
│
└── getResponseWithInterceptorChain()
│
└── 拦截器链 (Interceptor Chain)
核心组件
| 组件 | 职责 |
|---|---|
| OkHttpClient | 配置中心,连接池、超时、拦截器等,应单例复用 |
| RealCall | 实际执行请求的 Call 实现,封装一次请求的完整生命周期 |
| Dispatcher | 调度器,管理同步/异步请求队列与线程池 |
| Interceptor Chain | 拦截器链,按顺序处理请求与响应 |
二、请求入口:newCall → RealCall
// OkHttpClient.java
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
// RealCall.java
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
每次 newCall() 都会创建新的 RealCall,但应复用同一个 OkHttpClient。
三、execute():同步执行
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this); // 加入同步队列
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this); // 从队列移除
}
}
步骤说明
| 步骤 | 逻辑 | 说明 |
|---|---|---|
| 1 | synchronized + executed | 一个 Call 只能执行一次,重复执行抛 IllegalStateException |
| 2 | captureCallStackTrace() | 记录调用栈,用于泄漏检测(未关闭 Response 时提示) |
| 3 | dispatcher().executed(this) | 将 Call 加入 runningSyncCalls,便于 cancelAll() 等 |
| 4 | getResponseWithInterceptorChain() | 核心:走拦截器链获取响应 |
| 5 | dispatcher().finished(this) | 从队列移除,必要时触发 promoteCalls() |
四、enqueue():异步执行
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
// AsyncCall 内部
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (!signalledCallback) {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
Dispatcher 调度逻辑
// Dispatcher.java
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests
&& runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call); // 排队等待
}
}
- maxRequests:默认 64,最大并发请求数
- maxRequestsPerHost:默认 5,单主机最大并发
- 超出限制的请求进入
readyAsyncCalls,有请求完成时通过promoteCalls()再执行
五、拦截器链:getResponseWithInterceptorChain()
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); // 应用拦截器
interceptors.add(retryAndFollowUpInterceptor); // 重试与重定向
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors()); // 网络拦截器
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, ...);
return chain.proceed(originalRequest);
}
拦截器顺序与职责
| 顺序 | 拦截器 | 职责 |
|---|---|---|
| 1 | Application Interceptors | 用户自定义,可修改请求/响应,只执行一次 |
| 2 | RetryAndFollowUpInterceptor | 重试、重定向、认证(407/401) |
| 3 | BridgeInterceptor | 补全 Header(Content-Type、Content-Length 等)、Cookie |
| 4 | CacheInterceptor | 缓存读写,命中则直接返回 |
| 5 | ConnectInterceptor | 建立连接(TCP/TLS),连接池复用 |
| 6 | Network Interceptors | 用户自定义,可看到真实网络请求,每个重试都会执行 |
| 7 | CallServerInterceptor | 真正发起 HTTP 请求,写请求体、读响应 |
RealInterceptorChain.proceed()
@Override public Response proceed(Request request) throws IOException {
// 创建下一个链节点(index + 1)
RealInterceptorChain next = new RealInterceptorChain(interceptors, ..., index + 1, ...);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next); // 调用当前拦截器
return response;
}
每个拦截器调用 chain.proceed(request) 时,会触发下一个拦截器,形成递归调用,最终由 CallServerInterceptor 发起真实网络请求。
六、关键拦截器说明
RetryAndFollowUpInterceptor
- 重试:连接失败、超时等可恢复异常时,换路由重试(最多 20 次重定向/认证)
- 重定向:301/302/307/308 等,自动跟随(可配置关闭)
- 认证:407 代理认证、401 未授权,调用
Authenticator - 不可重试:
UnrepeatableRequestBody(流式 body)、ProtocolException、证书错误等
CallServerInterceptor
- 写入请求头:
httpCodec.writeRequestHeaders(request) - 写入请求体:
request.body().writeTo(bufferedRequestBody) - 读取响应头:
httpCodec.readResponseHeaders(false) - 读取响应体:
httpCodec.openResponseBody(response)
七、整体执行流程图
用户调用
│
├── client.newCall(request).execute()
│ │
│ └── RealCall.execute()
│ │
│ ├── dispatcher.executed(this)
│ ├── getResponseWithInterceptorChain()
│ │ │
│ │ └── 拦截器链 proceed()
│ │ │
│ │ ├── Application Interceptors
│ │ ├── RetryAndFollowUpInterceptor
│ │ ├── BridgeInterceptor
│ │ ├── CacheInterceptor
│ │ ├── ConnectInterceptor
│ │ ├── Network Interceptors
│ │ └── CallServerInterceptor ──→ 真实网络 I/O
│ │
│ └── dispatcher.finished(this)
│
└── client.newCall(request).enqueue(callback)
│
└── dispatcher.enqueue(AsyncCall)
│
├── 未超限 ──→ executorService.execute(AsyncCall)
│ │
│ └── getResponseWithInterceptorChain()
│ └── callback.onResponse/onFailure
│
└── 超限 ──→ readyAsyncCalls 排队
八、使用注意事项
1. OkHttpClient 必须复用
// ✅ 正确:单例复用
val client = OkHttpClient()
client.newCall(request1).execute()
client.newCall(request2).execute()
// ❌ 错误:每次新建,浪费连接池和线程池
OkHttpClient().newCall(request).execute()
每个 Client 持有独立的连接池、线程池,复用可减少延迟和内存占用。
2. Call 只能执行一次
val call = client.newCall(request)
call.execute() // ✅
call.execute() // ❌ IllegalStateException: Already Executed
需要再次请求时,应 client.newCall(request) 创建新的 Call。
3. Response.body() 必须关闭
// ✅ 正确
val response = call.execute()
response.use {
val body = it.body?.string()
// 使用 body
}
// 或
response.body?.use { it.string() }
// ❌ 错误:不关闭会导致连接无法复用、内存泄漏
val body = response.body?.string() // 若未关闭流,连接泄漏
Response 持有连接资源,必须关闭 body 才能归还连接池。
4. 主线程禁止 execute()
// ❌ Android 主线程会触发 NetworkOnMainThreadException
val response = call.execute()
// ✅ 异步
call.enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) { ... }
override fun onFailure(call: Call, e: IOException) { ... }
})
Android 4.0+ 禁止在主线程进行网络 I/O,应使用 enqueue() 或配合协程/线程池。
5. 取消请求
val call = client.newCall(request)
call.enqueue(callback)
// 需要取消时
call.cancel()
cancel() 会中断正在进行的连接或流,但不会自动关闭已返回的 Response.body(),调用方仍需负责关闭。
6. 超时配置
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS) // 连接超时
.readTimeout(30, TimeUnit.SECONDS) // 读取超时
.writeTimeout(30, TimeUnit.SECONDS) // 写入超时
.build()
- connectTimeout:建立 TCP/TLS 连接
- readTimeout:等待服务器响应(含 body 读取)
- writeTimeout:写入请求体
7. Application vs Network Interceptors
| 类型 | 执行次数 | 可见范围 | 典型用途 |
|---|---|---|---|
| Application | 1 次(含重试) | 只看最终请求/响应 | 日志、全局 Header、加解密 |
| Network | 每次重试都会执行 | 可见真实网络请求(含重定向) | 网络层调试、监控 |
8. 生命周期与内存泄漏
- 在 Activity/Fragment 中发起请求时,应在
onDestroy中call.cancel(),避免回调时持有已销毁的 Context - 使用
response.body?.use { }或response.use { }确保资源释放
九、HTTPS 证书验证
OkHttp 的证书校验在 RealConnection.connectTls() 中完成,分为三层,依次执行。
9.1 校验流程概览
TLS 握手 (sslSocket.startHandshake())
│
├── 1. TrustManager:校验证书链(根 CA、中间 CA、叶子证书)
│
├── 2. HostnameVerifier:校验证书 CN/SAN 与请求域名是否一致
│
└── 3. CertificatePinner:校验证书公钥是否与预置 Pin 匹配
9.2 三层校验说明
(1)TrustManager(证书链校验)
在 sslSocket.startHandshake() 时,由 Java SSLSocketFactory 背后的 TrustManager 完成:
- 校验证书链是否由系统信任的根 CA 签发
- 校验证书是否过期、是否被吊销
- 校验签名、有效期等
失败会抛出 SSLHandshakeException。默认使用系统 TrustManager。
(2)HostnameVerifier(主机名校验)
握手成功后,校验证书中的主机名是否与请求域名一致:
// RealConnection.connectTls()
if (!address.hostnameVerifier().verify(address.url().host(), sslSocket.getSession())) {
throw new SSLPeerUnverifiedException("Hostname ... not verified");
}
- 检查证书的 CN(Common Name)和 SAN(Subject Alternative Names)
- 默认使用
OkHostnameVerifier(与浏览器规则一致)
失败会抛出 SSLPeerUnverifiedException。
(3)CertificatePinner(证书钉校验)
在主机名校验之后,检查证书公钥是否与预置 Pin 匹配:
address.certificatePinner().check(address.url().host(),
unverifiedHandshake.peerCertificates());
- 对证书公钥做 SHA-256(或 SHA-1)哈希,与配置的 Pin 比较
- 证书链中任意一个证书匹配即通过
- 未配置 Pin 的域名会跳过此步骤
失败会抛出 SSLPeerUnverifiedException。
9.3 校验顺序(源码)
// RealConnection.connectTls()
sslSocket.startHandshake(); // 1. TrustManager 校验证书链
Handshake unverifiedHandshake = Handshake.get(sslSocket.getSession());
if (!address.hostnameVerifier().verify(...)) // 2. 主机名校验
throw new SSLPeerUnverifiedException(...);
address.certificatePinner().check(...); // 3. 证书钉校验
9.4 配置方式
| 组件 | 配置方法 | 默认行为 |
|---|---|---|
| TrustManager | sslSocketFactory(sslSocketFactory) | 使用系统默认 TrustManager |
| HostnameVerifier | hostnameVerifier(verifier) | 使用 OkHostnameVerifier.INSTANCE |
| CertificatePinner | certificatePinner(pinner) | 默认 CertificatePinner.DEFAULT(不校验) |
9.5 CertificatePinner 使用示例
val certificatePinner = CertificatePinner.Builder()
.add("api.example.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.add("*.example.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
9.6 常见异常与含义
| 异常 | 通常原因 |
|---|---|
| SSLHandshakeException | 证书链无效、过期、根 CA 不受信任、协议/密码套件不匹配 |
| SSLPeerUnverifiedException | 主机名校验失败,或 CertificatePinner 校验失败 |
十、小结
| 环节 | 要点 |
|---|---|
| 入口 | OkHttpClient.newCall(request) 创建 RealCall |
| 执行 | execute() 同步阻塞,enqueue() 异步回调 |
| 调度 | Dispatcher 管理并发(默认 64 全局、5 每主机) |
| 核心 | getResponseWithInterceptorChain() 串联拦截器链 |
| 网络 | CallServerInterceptor 执行真实 HTTP 请求 |
| 证书 | TrustManager → HostnameVerifier → CertificatePinner 三层校验 |
| 复用 | 单例 OkHttpClient,关闭 Response.body,避免主线程 execute |