前面分析Retrofit的源码,Retrofit底层使用了OkHttp来做网络请求操作。在介绍ConnectInterceptor时有设计到OkHttp的连接池ConnectionPool。本篇文章将详细介绍其实现。
核心类
OkHttp的连接池主要涉及几个核心类,ConnectionPool.java
、RealConnection.java
、StreamAllocation.java
、Internal.java
-
ConnectionPool.java
这个类主要负责管理HTTP HTTP/2连接的重用以减少网络延时。它实现了连接重用、清理的的策略。 -
Internal.java
外部对连接池不直接操作,而是通过这个类来完成。这个类是一个抽象类,只有一个唯一的实现,就在OKHttpClient中。 -
RealConnection.java
这是真正处理物理连接的类。普通Socket的连接、SSL Socket的连接、IO的读写都是在这个类完成的。 -
StreamAllocation.java
这个类主要用于处理物理Sokect连接、Streams(逻辑上的Http Request流和Response流)以及Calls(请求数据,包括原始请求以及重定向的请求或者需要证书认证的请求)之间的关系。每个连接有它们自己的分配上限,这决定了每个连接能够承载多少路并发流。HTTP 1.x一次最多有一路流,HTTP/2可以有多路。
这个类提供了每一个连接以及每个连接里的流的释放的API。
这个类还可以提供取消异步请求的API。
初始化
连接池的初始化在OKHttpClient的Builder函数里面。
public static final class Builder {
...
ConnectionPool connectionPool;
...
public Builder() {
...
connectionPool = new ConnectionPool();
...
}
}
我们知道Java类的初始化顺序
再看下ConnectionPool的构造函数。
public ConnectionPool() {
this(5, 5, TimeUnit.MINUTES);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
// Put a floor on the keep alive duration, otherwise cleanup will spin loop.
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
初始化连接池的时候主要传入了2个参数。一个是maxIdleConnections,表示连接池中最大的空闲连接数,最大是5。keepAliveDuration表示空闲连接与服务器保持连接的时间,最大是5分钟。
put
put方法是在StreamAllocation类的findConnection方法里面调用的。findConnection已经在Android-Retrofit源码解析(一)调用流程(下)里分析过了。我们只看下相关的代码。
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
...
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
if (newRouteSelection) {
//1. 获取到IP地址的集合,然后根据IP地址去连接池里查找是否有可复用的连接。
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
List<Route> routes = routeSelection.getAll();
for (int i = 0, size = routes.size(); i < size; i++) {
Route route = routes.get(i);
Internal.instance.get(connectionPool, address, this, route);
if (connection != null) {
foundPooledConnection = true;
result = connection;
this.route = route;
break;
}
}
}
//2-1.如果连接池中没有找到可复用的连接,就创建一个连接
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = r