OkHttp 网络请求执行流程分析

0 阅读6分钟

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);          // 从队列移除
    }
}

步骤说明

步骤逻辑说明
1synchronized + executed一个 Call 只能执行一次,重复执行抛 IllegalStateException
2captureCallStackTrace()记录调用栈,用于泄漏检测(未关闭 Response 时提示)
3dispatcher().executed(this)将 Call 加入 runningSyncCalls,便于 cancelAll()
4getResponseWithInterceptorChain()核心:走拦截器链获取响应
5dispatcher().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);
}

拦截器顺序与职责

顺序拦截器职责
1Application Interceptors用户自定义,可修改请求/响应,只执行一次
2RetryAndFollowUpInterceptor重试、重定向、认证(407/401)
3BridgeInterceptor补全 Header(Content-Type、Content-Length 等)、Cookie
4CacheInterceptor缓存读写,命中则直接返回
5ConnectInterceptor建立连接(TCP/TLS),连接池复用
6Network Interceptors用户自定义,可看到真实网络请求,每个重试都会执行
7CallServerInterceptor真正发起 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

类型执行次数可见范围典型用途
Application1 次(含重试)只看最终请求/响应日志、全局 Header、加解密
Network每次重试都会执行可见真实网络请求(含重定向)网络层调试、监控

8. 生命周期与内存泄漏

  • 在 Activity/Fragment 中发起请求时,应在 onDestroycall.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 配置方式

组件配置方法默认行为
TrustManagersslSocketFactory(sslSocketFactory)使用系统默认 TrustManager
HostnameVerifierhostnameVerifier(verifier)使用 OkHostnameVerifier.INSTANCE
CertificatePinnercertificatePinner(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