因为Okhttp中拦截器都是责任链设计模式,这里直接看intercept()方法即可,先来献上高清无码图,方便更好的理解其原理。
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//RetryAndFollowUpInterceptor中创建
StreamAllocation streamAllocation = realChain.streamAllocation();
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//看来核心代码是newStream();获取到httpCodec
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
//按照字面意思是获取一个连接
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
可以看出它的核心方法为:streamAllocation.newStream(client, chain, doExtensiveHealthChecks) 那么该方法到底做了什么事?
public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
int connectTimeout = chain.connectTimeoutMillis();
int readTimeout = chain.readTimeoutMillis();
int writeTimeout = chain.writeTimeoutMillis();
int pingIntervalMillis = client.pingIntervalMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
...
return resultCodec;
}
}
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);
// If this is a brand new connection, we can skip the extensive health checks.
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
...
return candidate;
}
到这里还是没有发现具体怎么找到的连接,可以看到上面有一个findConnection()方法,再来看看这个方法是具体如何找到连接的?
RealConnection result = null;
Route selectedRoute = null;
Connection releasedConnection;
...
//从连接池中获取可用的连接
if (result == null) {
// Attempt to get a connection from the pool.
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
foundPooledConnection = true;
result = connection;
} else {
selectedRoute = route;
}
}
if (result != null) {
// If we found an already-allocated or pooled connection, we're done.
return result;
}
...
synchronized (connectionPool) {
...
if (newRouteSelection) {
//获取路由表中的信息,并且缓存起来
List 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;
break;
}
}
}
...
//如果从缓存中更没有找到连接,则new 一个新的连接
result = new RealConnection(connectionPool, selectedRoute);
acquire(result, false);
}
}
//http的三次握手操作 Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
reportedAcquired = true;
//将连接放入连接池 Pool the connection.
Internal.instance.put(connectionPool, result);
}
...
return result;
}
即如果找到从连接池中获取可用的连接,如果没有则创建,并且重新缓存到连接池中。
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
可以看到newCodec中所做的事,可以看出是将streamAllocation和source,sink做了封装,可以看到这里用到了okio中的流的封装。
public HttpCodec newCodec(OkHttpClient client, Interceptor.Chain chain,
StreamAllocation streamAllocation) throws SocketException {
if (http2Connection != null) {
return new Http2Codec(client, chain, streamAllocation, http2Connection);
} else {
socket.setSoTimeout(chain.readTimeoutMillis());
source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
return new Http1Codec(client, streamAllocation, source, sink);
}
}
RealConnection result = new RealConnection(connectionPool, selectedRoute);
new RealConnection()做了什么事,可以从源码中看出,里面有好几个connetXX的方法和地址的封装,但是connetXX方法中都会有socket和okio的封装,即如下代码connectTls()方法实现:
private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
Address address = route.address();
SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
boolean success = false;
SSLSocket sslSocket = null;
// Create the wrapper over the connected socket.
sslSocket = (SSLSocket) sslSocketFactory.createSocket(
rawSocket, address.url().host(), address.url().port(), true /* autoClose */);
// Configure the socket's ciphers, TLS versions, and extensions.
ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
if (connectionSpec.supportsTlsExtensions()) {
Platform.get().configureTlsExtensions(
sslSocket, address.url().host(), address.protocols());
}
// Force handshake. This can throw!
sslSocket.startHandshake();
// block for session establishment
SSLSession sslSocketSession = sslSocket.getSession();
Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
// Check that the certificate pinner is satisfied by the certificates presented.
address.certificatePinner().check(address.url().host(),
unverifiedHandshake.peerCertificates());
// Success! Save the handshake and the ALPN protocol.
String maybeProtocol = connectionSpec.supportsTlsExtensions()
? Platform.get().getSelectedProtocol(sslSocket)
: null;
socket = sslSocket;
source = Okio.buffer(Okio.source(socket));
sink = Okio.buffer(Okio.sink(socket));
handshake = unverifiedHandshake;
protocol = maybeProtocol != null
? Protocol.get(maybeProtocol)
: Protocol.HTTP_1_1;
success = true;
}
到这里可以得出如下结论:OkHttp 是基于原生的 Socket + okio(原生IO的封装)
HttpCodec 里面封装了 okio 的 Source(输入) 和 Sink (输出),我们通过 HttpCodec 就可以操作 Socket的输入输出,可以向服务器写数据和读取返回数据
ConnectionInterceptor是获取一个RealConnection对象,然后创建Socket链接,封装地址信息,然后调用CallServerInterceptor 来完成Okhttp的整个操作。