一、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 是实际实现,内部持有 client 和 originalRequest。
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
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 { if (index >= interceptors.size()) throw new AssertionError();
calls++;
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);
if (response == null) { throw new NullPointerException("interceptor " + interceptor + " returned null"); } return response; } }
|
责任链模式的精髓:每个拦截器的 intercept(Chain) 方法接收 chain 对象,先做前置处理,再调用 chain.proceed(request) 将请求传给下一个拦截器(从而继续沿链传播),拿到响应后再做后置处理。
2.2 各拦截器源码深度
RetryAndFollowUpInterceptor(重试和重定向):
@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(); }
if (++followUpCount > MAX_FOLLOW_UPS) { throw new ProtocolException("Too many follow-up requests: " + followUpCount); }
request = followUp; priorResponse = response; } }
|
ConnectInterceptor(连接建立):
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() 来获取一个可用连接:
public RealConnection find() { RealConnection pooledConnection = connectionPool.transmitterAcquirePooledConnection(address, transmitter); realConnection.connect(connectTimeout, readTimeout, writeTimeout, ...); }
|
CallServerInterceptor(写入请求和读取响应):
@Override public Response intercept(Chain chain) throws IOException { exchange.writeRequestHeaders(request);
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { exchange.writeRequestBody(request); }
exchange.finishRequest();
Response.Builder responseBuilder = exchange.readResponseHeaders(false);
Response response = responseBuilder .request(request) .handshake(connection.handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build();
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 对象的复用和清理。
public final class ConnectionPool { final RealConnectionPool delegate;
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) { this.delegate = new RealConnectionPool( maxIdleConnections, keepAliveDuration, timeUnit ); } }
|
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; }
|
连接清理通过后台线程定期执行:
连接池判断连接可复用的条件:
- 目标地址(Address)相同(scheme、host、port 一致)。
- 连接未被过度使用(连接上的 stream 数未超限)。
- 对于 HTTP/2,要求服务器的 SSL 证书一致。
四、DNS 解析与 IP 轮换
OkHttp 的 DNS 解析是可插拔的,默认使用系统的 InetAddress.getAllByName(),但可通过 Dns 接口替换:
Dns SYSTEM = hostname -> { List<InetAddress> addresses = DnsCompanion.getAllByName(hostname); if (addresses.isEmpty()) throw new UnknownHostException(hostname); return addresses; };
class HttpDns implements Dns { @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException { String ip = HttpDnsService.getIpByHost(hostname); return Collections.singletonList(InetAddress.getByName(ip)); } }
OkHttpClient client = new OkHttpClient.Builder() .dns(new HttpDns()) .build();
|
OkHttp 在 RouteSelector 中实现 IP 轮换:
public final class RouteSelector { private final Address address; private final RouteDatabase routeDatabase; private final Call call; private final EventListener eventListener;
private void resetNextInetSocketAddress(Proxy proxy) { Collections.shuffle(inetSocketAddresses); } }
|
RouteDatabase 记录了连接失败的路由,OkHttp 会避开这些路由。这实现了自动故障转移——当一个 IP 不可达时,自动尝试下一个 IP。
五、HTTP/2 多路复用
OkHttp 通过 Http2Connection 实现 HTTP/2 协议。HTTP/2 的主要优势:
- 多路复用(Multiplexing):一个 TCP 连接上并发多个请求流(Stream),无需建立多个连接。
- 头部压缩(HPACK):减少请求头大小。
- 服务器推送(Server Push):服务器可以主动推送资源。
synchronized int openStreamCount() { return nextStreamId / 2; }
|
六、Retrofit:声明式 HTTP 客户端
Retrofit(https://github.com/square/retrofit)是对 OkHttp 的高层封装,通过动态代理将接口方法调用转换为 HTTP 请求。
6.1 动态代理核心
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 { if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } args = args != null ? args : emptyArgs; Platform platform = Platform.get(); return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); } }); }
|
6.2 ServiceMethod 的注解解析
final class ServiceMethod<R, T> { final okhttp3.Call.Factory callFactory; final CallAdapter<R, T> callAdapter; private final HttpServiceMethod<?, ?> httpServiceMethod;
}
|
6.3 Converter Factory
Converter 负责将 Java/Kotlin 对象序列化为请求体,以及将响应体反序列化为对象:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(ProtoConverterFactory.create()) .build();
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 等异步模型:
class RxJava3CallAdapter implements CallAdapter<Object, Object> { @Override public Object adapt(Call<Object> call) { Observable<Response<R>> observable = new CallEnqueueObservable<>(call); if (isSingle) { return new SingleFromObservable<>(observable); } return observable; } }
class SuspendCallAdapterFactory { }
|
七、网络框架的性能优化策略
- 连接复用:使用 OkHttp 单例,全局共享连接池,避免重复 TCP/TLS 握手。
- DNS 缓存:OkHttp 默认不缓存 DNS 结果(每次都是新请求),可通过自定义 Dns + 内存缓存实现 DNS 缓存。
- HTTP/2 开启:服务器支持时,自动升级到 HTTP/2,复用连接。
- 响应缓存:通过
Cache 类启用 HTTP 缓存,遵循 HTTP Cache-Control 头控制。
- 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