目录
  1. 1. 一、OkHttp 的核心架构
  2. 2. 二、拦截器链(Interceptor Chain)
    1. 2.1. 2.1 拦截器链的实现:RealInterceptorChain
    2. 2.2. 2.2 各拦截器源码深度
  3. 3. 三、连接池(ConnectionPool)
  4. 4. 四、DNS 解析与 IP 轮换
  5. 5. 五、HTTP/2 多路复用
  6. 6. 六、Retrofit:声明式 HTTP 客户端
    1. 6.1. 6.1 动态代理核心
    2. 6.2. 6.2 ServiceMethod 的注解解析
    3. 6.3. 6.3 Converter Factory
    4. 6.4. 6.4 CallAdapter Factory
  7. 7. 七、网络框架的性能优化策略
  8. 8. 八、面试常问题目
解读开源框架系列-网络访问框架设计

一、OkHttp 的核心架构

OkHttp(https://github.com/square/okhttp)是 Square 开源的 HTTP 客户端库,是 Android 网络层的基石。早期的 Android 网络请求基于 HttpURLConnection(底层是 Apache HttpClient),OkHttp 重写了整个 HTTP 协议栈,提供了连接池、请求重试、HTTP/2 支持、WebSocket 等高级特性。

OkHttp 的核心架构围绕三个对象展开:

  • OkHttpClient:全局单例,持有连接池、调度器、拦截器链、缓存等。通过 Builder 模式创建。
  • Request / Response:表示 HTTP 请求和响应的不可变对象。
  • Call:一次网络请求的执行单元。RealCall 是实际实现,内部持有 clientoriginalRequest
// OkHttpClient 的创建(包含所有配置)
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES))
.addInterceptor(new LoggingInterceptor())
.build();

二、拦截器链(Interceptor Chain)

拦截器是 OkHttp 最精妙的设计。网络请求的整个生命周期通过一条拦截器链实现,每个拦截器负责一个独立的关注点。

OkHttp 内置的五大拦截器(按链中顺序):

用户自定义 Interceptor (addInterceptor)

RetryAndFollowUpInterceptor — 重试和重定向

BridgeInterceptor — 桥接(添加标准 Header,如 Content-Type、Cookie)

CacheInterceptor — 缓存(根据 Cache-Control 返回缓存或发出请求)

ConnectInterceptor — 建立连接(从连接池获取或创建新连接)

用户自定义 NetworkInterceptor (addNetworkInterceptor)

CallServerInterceptor — 写入请求体、读取响应体(与服务器实际交互)

2.1 拦截器链的实现:RealInterceptorChain

// okhttp3/internal/http/RealInterceptorChain.java(简化)
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;
private final int index; // 当前拦截器下标
private final Request request;
private final Call call;
private final int connectTimeout;
private final int readTimeout;
private final int writeTimeout;
private int calls; // 已执行的拦截器数量

public RealInterceptorChain(List<Interceptor> interceptors, int index,
Request request, Call call, ...) {
this.interceptors = interceptors;
this.index = index;
this.request = request;
this.call = call;
// ...
}

@Override
public Response proceed(Request request) throws IOException {
// 检查是否在同一链上多次调用 proceed(不安全)
if (index >= interceptors.size()) throw new AssertionError();

calls++;

// 创建下一个链:index + 1,指向下一个拦截器
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

// 验证响应不为 null
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
}

责任链模式的精髓:每个拦截器的 intercept(Chain) 方法接收 chain 对象,先做前置处理,再调用 chain.proceed(request) 将请求传给下一个拦截器(从而继续沿链传播),拿到响应后再做后置处理。

2.2 各拦截器源码深度

RetryAndFollowUpInterceptor(重试和重定向):

// okhttp3/internal/http/RetryAndFollowUpInterceptor.java
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();

int followUpCount = 0;
Response priorResponse = null;

while (true) {
transmitter.prepareToConnect(request);

if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}

Response response;
boolean success = false;
try {
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// 路由异常(无法通过代理/隧道连接)
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// 连接异常 → 尝试恢复(可能更换路由重试)
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
if (!success) {
transmitter.exchangeDoneDueToException();
}
}

// 处理重定向
Request followUp;
try {
followUp = followUpRequest(response, transmitter.route());
} catch (IOException e) {
transmitter.exchangeDoneDueToException();
throw e;
}
if (followUp == null) {
if (transmitter.exchange != null && transmitter.exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response; // 不需要重定向,返回响应
}

// 如果响应体闭合了就不能重定向
if (!transmitter.exchange.response.body.isOneShot()) {
transmitter.exchange.response.close();
}

// 最多重定向 20 次
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}

request = followUp;
priorResponse = response;
}
}

ConnectInterceptor(连接建立):

// okhttp3/internal/connection/ConnectInterceptor.java
public final class ConnectInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Exchange exchange = realChain.transmitter().newExchange(chain);

return realChain.proceed(realChain.request(), realChain.transmitter(), exchange);
}
}

ConnectInterceptor 看起来很简短,但真正的连接建立发生在 Transmitter.newExchange() 方法中,它调用 ExchangeFinder.find() 来获取一个可用连接:

// okhttp3/internal/connection/ExchangeFinder.java
public RealConnection find() {
// 1. 尝试从 transmitter 中复用已有连接
// 2. 尝试从连接池中获取连接
RealConnection pooledConnection = connectionPool.transmitterAcquirePooledConnection(address, transmitter);
// 3. 如果池中没有,创建新连接
// 4. 执行 TCP + TLS 握手
realConnection.connect(connectTimeout, readTimeout, writeTimeout, ...);
}

CallServerInterceptor(写入请求和读取响应):

// okhttp3/internal/http/CallServerInterceptor.java
@Override
public Response intercept(Chain chain) throws IOException {
// 1. 写入请求头
exchange.writeRequestHeaders(request);

// 2. 判断是否有请求体(POST、PUT 等)
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
exchange.writeRequestBody(request);
}

exchange.finishRequest();

// 3. 读取响应头
Response.Builder responseBuilder = exchange.readResponseHeaders(false);

// 4. 读取响应体
Response response = responseBuilder
.request(request)
.handshake(connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

// 5. 对于 WebSocket 升级请求不读取 body
if (response.code() == 101) {
response = response.newBuilder().body(Util.EMPTY_RESPONSE).build();
} else {
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}
return response;
}

三、连接池(ConnectionPool)

OkHttp 的连接复用是其性能优势的关键。连接池在底层管理 RealConnection 对象的复用和清理。

// okhttp3/ConnectionPool.java(简化)
public final class ConnectionPool {
final RealConnectionPool delegate;

public ConnectionPool(int maxIdleConnections, long keepAliveDuration,
TimeUnit timeUnit) {
this.delegate = new RealConnectionPool(
maxIdleConnections, // 最大空闲连接数(默认 5)
keepAliveDuration, // 保持时间(默认 5 分钟)
timeUnit
);
}
}
// okhttp3/internal/connection/RealConnectionPool.java
// 连接复用逻辑
boolean transmitterAcquirePooledConnection(Address address, Transmitter transmitter,
List<Route> routes, boolean requireMultiplexed) {
for (RealConnection connection : connections) {
if (connection.isEligible(address, routes, requireMultiplexed)) {
transmitter.acquireConnectionNoEvents(connection);
return true;
}
}
return false;
}

连接清理通过后台线程定期执行:

// 后台清理任务(使用 okhttp 内部的 TaskRunner)
// 每 keepAliveDuration / 3 执行一次清理
// 清理逻辑:找到空闲时间最长的连接,如果超过 keepAliveDuration 则关闭

连接池判断连接可复用的条件:

  1. 目标地址(Address)相同(scheme、host、port 一致)。
  2. 连接未被过度使用(连接上的 stream 数未超限)。
  3. 对于 HTTP/2,要求服务器的 SSL 证书一致。

四、DNS 解析与 IP 轮换

OkHttp 的 DNS 解析是可插拔的,默认使用系统的 InetAddress.getAllByName(),但可通过 Dns 接口替换:

// OkHttp 内置的 DNS 实现
// okhttp3/internal/-Dns.kt
Dns SYSTEM = hostname -> {
List<InetAddress> addresses = DnsCompanion.getAllByName(hostname);
if (addresses.isEmpty()) throw new UnknownHostException(hostname);
return addresses;
};

// 自定义 DNS(如 HTTPDNS)
class HttpDns implements Dns {
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
// 通过 HTTP DNS 服务获取 IP(绕过运营商 LocalDNS 劫持)
String ip = HttpDnsService.getIpByHost(hostname);
return Collections.singletonList(InetAddress.getByName(ip));
}
}

OkHttpClient client = new OkHttpClient.Builder()
.dns(new HttpDns())
.build();

OkHttp 在 RouteSelector 中实现 IP 轮换:

// okhttp3/internal/connection/RouteSelector.java
public final class RouteSelector {
private final Address address;
private final RouteDatabase routeDatabase; // 记录失败的路由
private final Call call;
private final EventListener eventListener;

// 选择下一个路由
// 1. 先遍历代理(Proxy)
// 2. 再遍历 IP 地址(由 Dns.lookup 返回的列表)
// 3. 跳过 routeDatabase 中标记为失败的路由
private void resetNextInetSocketAddress(Proxy proxy) {
// 打乱 IP 地址顺序(负载均衡)
Collections.shuffle(inetSocketAddresses);
}
}

RouteDatabase 记录了连接失败的路由,OkHttp 会避开这些路由。这实现了自动故障转移——当一个 IP 不可达时,自动尝试下一个 IP。

五、HTTP/2 多路复用

OkHttp 通过 Http2Connection 实现 HTTP/2 协议。HTTP/2 的主要优势:

  1. 多路复用(Multiplexing):一个 TCP 连接上并发多个请求流(Stream),无需建立多个连接。
  2. 头部压缩(HPACK):减少请求头大小。
  3. 服务器推送(Server Push):服务器可以主动推送资源。
// okhttp3/internal/http2/Http2Connection.java
// HTTP/2 的连接管理——多个 Stream 共享一个 TCP 连接
synchronized int openStreamCount() {
// Stream ID 为奇数的由客户端发起(1, 3, 5, ...)
return nextStreamId / 2;
}

// 每个 HTTP/2 Stream 映射到一个 Http2Stream 对象
// 不同的请求 (RealCall) 绑定到不同的 Stream,共享同一个 Http2Connection

六、Retrofit:声明式 HTTP 客户端

Retrofit(https://github.com/square/retrofit)是对 OkHttp 的高层封装,通过动态代理将接口方法调用转换为 HTTP 请求。

6.1 动态代理核心

// retrofit2/Retrofit.java
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T) Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[]{service},
new InvocationHandler() {
private final Object[] emptyArgs = new Object[0];

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// Object 类的方法(equals、hashCode、toString)直接处理
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
Platform platform = Platform.get();
// 默认方法(Java 8+)使用 default 实现
// 否则:解析注解 → 构建 ServiceMethod → 调用 OkHttp
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}

6.2 ServiceMethod 的注解解析

// retrofit2/ServiceMethod.java(简化)
final class ServiceMethod<R, T> {
final okhttp3.Call.Factory callFactory;
final CallAdapter<R, T> callAdapter;
private final HttpServiceMethod<?, ?> httpServiceMethod;

// 解析方法注解 → 构造 RequestFactory
// 解析 @GET、@POST → URL template
// 解析 @Path、@Query、@Body → 参数绑定
// 解析 @Headers → 请求头
}

6.3 Converter Factory

Converter 负责将 Java/Kotlin 对象序列化为请求体,以及将响应体反序列化为对象:

// 注册 Converter
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com")
.addConverterFactory(GsonConverterFactory.create()) // JSON
.addConverterFactory(ProtoConverterFactory.create()) // Protobuf
.build();

// GsonConverterFactory 的实现(简化)
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;

@Override
public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}

6.4 CallAdapter Factory

CallAdapter 负责将 OkHttp 的 Call 适配为 RxJava、Coroutines 等异步模型:

// RxJava3CallAdapterFactory 的核心处理
class RxJava3CallAdapter implements CallAdapter<Object, Object> {
@Override
public Object adapt(Call<Object> call) {
// 将 Call<T> 包装为 Observable<T> / Single<T> / Flowable<T> 等
Observable<Response<R>> observable = new CallEnqueueObservable<>(call);
if (isSingle) {
return new SingleFromObservable<>(observable);
}
return observable;
}
}

// Kotlin Coroutines 的 CallAdapter
class SuspendCallAdapterFactory {
// 将 Call<T> 适配为 suspend 函数
// 内部使用 kotlin.coroutines 的 suspendCancellableCoroutine
}

七、网络框架的性能优化策略

  1. 连接复用:使用 OkHttp 单例,全局共享连接池,避免重复 TCP/TLS 握手。
  2. DNS 缓存:OkHttp 默认不缓存 DNS 结果(每次都是新请求),可通过自定义 Dns + 内存缓存实现 DNS 缓存。
  3. HTTP/2 开启:服务器支持时,自动升级到 HTTP/2,复用连接。
  4. 响应缓存:通过 Cache 类启用 HTTP 缓存,遵循 HTTP Cache-Control 头控制。
  5. Gzip 解压:BridgeInterceptor 自动添加 Accept-Encoding: gzip 头。

八、面试常问题目

Q1: OkHttp 的拦截器链是如何实现的?addInterceptor 和 addNetworkInterceptor 有什么区别?

拦截器链使用责任链模式,每个拦截器的 intercept() 方法接收 Chain 对象(指向下一个拦截器),调用 chain.proceed(request) 将请求传递给下一个拦截器。addInterceptor 的拦截器在 RetryAndFollowUpInterceptor 之前执行,是”应用层拦截器”,可以拦截所有请求(包括重定向产生的请求)。addNetworkInterceptor 的拦截器在 ConnectInterceptor 之后、CallServerInterceptor 之前,是”网络层拦截器”,只能拦截实际发送到服务器的请求(不拦截缓存命中的请求),且可以观测到连接信息(Connection)。

Q2: OkHttp 的连接池是如何管理连接的?

RealConnectionPool 维护了一个 ArrayDeque<RealConnection>,按空闲时间排序。当需要连接时,遍历队列查找地址匹配且状态可用的连接。后台清理线程定期运行,关闭超过 keepAliveDuration 的空闲连接,且保留至少 minIdleConnections 个连接。HTTP/2 的连接可承载多个 Stream,复用度远高于 HTTP/1.1。

Q3: Retrofit 是如何将接口方法转换为 HTTP 请求的?

Retrofit 使用 Java 动态代理(Proxy.newProxyInstance),拦截接口方法调用。通过反射解析方法上的 HTTP 注解(@GET、@POST 等)和参数注解(@Path、@Query、@Body 等),构建 RequestFactory。然后通过 Converter 将参数序列化(如 Java 对象转 JSON),通过 CallAdapter 将 OkHttp Call 适配为 Observable/Single/suspend 等返回类型。整个过程在 ServiceMethod.invoke() 中将注解解析结果和实际参数结合,生成完整的 OkHttp Request 并执行。

Q4: HTTP/2 多路复用解决了什么问题?

HTTP/1.x 的”队头阻塞”(Head-of-Line Blocking)问题:一个 TCP 连接上一次只能处理一个请求/响应,如果前一个请求慢(如大文件下载),后续请求会被阻塞。虽然可以用多个并发连接缓解,但每个连接需要独立的 TCP/TLS 握手,成本高昂。HTTP/2 通过多路复用在单个 TCP 连接上并发多个 Stream,每个 Stream 独立传输,解决了队头阻塞,同时减少了连接开销。


参考源码路径:

  • OkHttp 仓库:https://github.com/square/okhttp
  • 拦截器链:okhttp/src/main/kotlin/okhttp3/internal/http/RealInterceptorChain.kt
  • Retry 拦截器:okhttp/src/main/kotlin/okhttp3/internal/http/RetryAndFollowUpInterceptor.kt
  • 连接池:okhttp/src/main/kotlin/okhttp3/internal/connection/RealConnectionPool.kt
  • 连接查找:okhttp/src/main/kotlin/okhttp3/internal/connection/ExchangeFinder.kt
  • Retrofit 仓库:https://github.com/square/retrofit
  • 动态代理:retrofit/src/main/java/retrofit2/Retrofit.java
  • CallAdapter:retrofit/src/main/java/retrofit2/CallAdapter.java
  • HTTP/2 实现:okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Connection.kt
打赏
  • 微信
  • 支付宝

评论