源码分析基于:OkHttp3.14.7进行。
对于Android开发者而言,网络请求不可避免。我们使用的最多的就是Square的OkHttp3框架。关于这个框架使用的时间很长,正好前一段时间有点时间,就利用业余时间仔细阅读了该框架的源码,现在就把阅读过程中自己的感悟写下来分享给大家。
对于OkHttp3有两种网络请求的方式,一种是同步请求的方式;一种就是异步请求的方式。
示例代码如下:
private void synGet() {
final OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
mHttpPool.execute(new Runnable() {
@Override
public void run() {
try {
final Response response = client.newCall(request).execute();
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
对于同步请求是将请求直接放进执行队列进行执行。
private void asynGet() {
final OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
final Call call = client.newCall(request);
//
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//失败
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final ResponseBody body = response.body();
if (!response.isSuccessful()) {
throw new IOException("Unexcepted code " + response);
}
//请求头
final Headers headers = response.headers();
final int count = headers.size();
for (int i = 0; i < count; i++) {
Log.d(TAG, headers.name(i) + " : " + headers.value(i));
}
Log.d(TAG, "result : " + body.string());
}
});
}
异步请求是将网络请求添加进入队列等待执行。
我们既然使用了OkHttp3中的这两种网络请求。那么我们就需要配知道它是怎么做到的,怎么讲用户的请求 参数进行的封装,怎么将用户的请求进行的传递,以及怎么讲网络请求传递给服务器,以及需不需要握手啊等等吧,在这里就不进行逐一列举了。
我们先从同步请求说起吧。具体是的使用方法见第一部分代码示例,在这里就不再说明。我们直接看在线程中执行的执行网络请求获取服务器相应的代码:“Response response = client.newCall(request).execute()”,在这段代码中首先是讲Request作为参数获取的RealCall对象,Real是Call接口的实现类,一方面持有已准备就绪的特定网络请求(Request)信息,另一方面持有Transmitter对象驱动网络请求并将本次网络请求(call)添加到请求分发器(Dispatcher)的集合中。同时,RealCall中添加了网络请求相关的拦截器。然后调用了RealCall中的execute()方法,获取的服务器相应数据。
我们接下来看一下execute()方法的源码:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
client.dispatcher().executed(this);
return getResponseWithInterceptorChain();
} finally {
client.dispatcher().finished(this);
}
}
该方法主要逻辑如下:
1>保证每一个call只能执行一次,通过标志变量标记执行的状态,当已经执行过就会抛出异常;
2>网络请求的执行:client.dispatcher().executed(this)是真正的执行该网络请求,通过OkHttpClient获取Dispatcher对象,关于Dispatcher我们不在这里解释,只需要知道它是网络请求执行的策略就可以了。
3>添加拦截器和获取响应:最终我们通过getResponseWithInterceptorChain()方法为该call添加各种拦截器和获取请求的响应;
4>最终通过Dispatcher的对象调用finish方法,进行网络请求完成的相关操作。
关于 添加拦截器以及获取相应源码如下:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
//失败重试和重定向拦截器;
interceptors.add(new RetryAndFollowUpInterceptor(client));
//负责将用户请求转换为服务器请求,并返回用户友好的数据响应;
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, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
//执行网络请求
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
我们进行的每一个网络请求都是通过该方法进行相应拦截器的添加逻辑。关于拦截器相关的代码解析我们会在后面的部门进行。
关于使用OkHttp进行异步请求网络数据的代码这里就不再多说了。它也是首先获得RealCall对象,然后通过RealCall的enqueue()方法进行的。下面我们就来分析一下enqueue()方法的源码:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
//将异步请求添加进队列等待执行;
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
该方法是将AsyncCall(异步请求)添加进队列等待执行;下面通过Dispatcher的enqueue()方法的将AsyncCall添加进队列(readyAsyncCalls准备就绪的队列),并通过promoteAndExecute()方法将准备就绪的Call从readyAsyncCalls中添加进runningAsyncCalls(正在执行的队列)中,并进行执行。我们看一下promoteAndExecute()方法的源码:
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
从上面我们看到最终是调用AsyncCall的executeOn()方法执行的,我们通过AsyncCall的源码知道,其本质就是一个线程,其executeOn()方法的源码如下:
/**
* Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
//调用该线程的run()方法执行。
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
//执行完成
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
在该方法中,通过executorService运行了该线程,我们通过代码知道AsyncCall继承于NamedRunnable,在其父类中的run()方法中调用了execute()方法,我们通过AsyncCall的该方法中的代码中知道, 最终也调用了该方法getResponseWithInterceptorChain()添加拦截器和获取网络请求的相应(Response).关于getResponseWithInterceptorChain()方法中的逻辑分析,请看同步请求部分。
我们通过第二部分了解到使用OkHttp进行网络请求需要添加一些列的拦截器,关于这些拦截器的作用就是这部分的主要内容,也是OkHttp这个框架的重点。我们回归到第二部分中的getResponseWithInterceptorChain()方法,在该方法中新建了一个RealInterceptorChain对象chain,而拦截器数组就是作为其参数,看来该类就是主要处理拦截器的逻辑的。下面通过chain对象执行chain.proceed(originalRequest)方法获取了Response对象。下面我们就看一下proceed()方法,看一看OkHttp是怎么处理这些拦截器的。源码如下:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
//如果我们已经有一个数据流,确定即将到来的Request会使用它;
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
//保证该拦截器只调用过一次;
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
//获取并调用下一个拦截器;
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
//执行对应拦截器的intercept()方法
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
//确保下一个拦截器没有调用过;
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
该方法确保拦截器链中的拦截器能准确无误的逐一执行,也就是责任链模式的体现;
该拦截器从连接失败中恢复,并根据需要进行重定向。如果网络请求取消了有可能会抛出IOException.
重定向:通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)。
@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) {
// The attempt to connect via a route failed. The request will not have been sent.
//报告并不再尝试去恢复故障后的与服务器之间的通信;
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
//报告并可能尝试去恢复故障后的与服务器之间的通信;
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// The network call threw an exception. Release any resources.
if (!success) {
transmitter.exchangeDoneDueToException();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
//失败重试以及重定向
Request followUp = followUpRequest(response, route);
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}
//进行次数限制
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
request = followUp;
priorResponse = response;
}
}
关于这里怎么进行重试以及重定向的没有理解其思路。
该拦截器从应用代码到网络代码的桥梁。首先它根据用户请求转化为网络请求。然后它会执行网络请求。最终它会将网络响应转化为用户响应。
@Override public Response intercept(Chain chain) throws IOException {
//用户请求
Request userRequest = chain.request();
//创建新的请求
Request.Builder requestBuilder = userRequest.newBuilder();
//请求体
RequestBody body = userRequest.body();
if (body != null) {
//请求体添加Content-Type
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
//请求体添加Content-Length
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
//添加Host
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
//添加Header:Connection
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
//添加Header:Accept-Encoding,如果添加了"Accept-Encoding: gzip"header,则需要解压缩传输流;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
//添加Header:Cookie
List cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
//添加Header:User-Agent
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
//生成新的Response
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
该方法就是起到了将用户请求转化为网络请求的作用,并将网络的响应转化为用户响应。
该拦截器将请求从缓存中取出,并将响应写入缓存;
@Override public Response intercept(Chain chain) throws IOException {
//如果存在缓存,则获取存储的Response;
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//缓存策略
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
//跟踪满足当前策略的响应(Response)
if (cache != null) {
cache.trackResponse(strategy);
}
//缓存不适用则关闭
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// If we're forbidden from using the network and the cache is insufficient, fail.
//如果禁用网络并且缓存不足则失败~
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// If we don't need the network, we're done.
//如果不使用网络,则返回body为nul的Response;
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
//更新缓存
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
该拦截器负责建立一个指向指定服务的连接并执行下一个拦截器;
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
Transmitter transmitter = realChain.transmitter();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//创建新的Exchange对象,并建立与服务器的连接。
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
return realChain.proceed(request, transmitter, exchange);
}
方法中Exchange是用来传递单一的Request和Response对,具体的连接管理和事件相关是在ExchangeCodec层中。具体的连接建立逻辑还需要分析transmitter.newExchange()方法,这部分将在后面的分析中介绍。
这是最后一个拦截器,它负责向服务器发送一个指定的网络请求。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Exchange exchange = realChain.exchange();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
exchange.writeRequestHeaders(request);
boolean responseHeadersStarted = false;
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
//如果请求(Request)中存在"Expect: 100-continue"这样的请求头,就需要在发送请求正文(request body)
//之前等待"HTTP/1.1 100 Continue"的响应。如果我们得不到该相应,则返回我们得到的结果(例如4**的响应)
//而无需发送请求主体。
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
responseHeadersStarted = true;
exchange.responseHeadersStart();
responseBuilder = exchange.readResponseHeaders(true);
}
if (responseBuilder == null) {
if (request.body().isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest();
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, true));
request.body().writeTo(bufferedRequestBody);
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, false));
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
} else {
exchange.noRequestBody();
if (!exchange.connection().isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
exchange.noNewExchangesOnConnection();
}
}
} else {
exchange.noRequestBody();
}
if (request.body() == null || !request.body().isDuplex()) {
exchange.finishRequest();
}
if (!responseHeadersStarted) {
exchange.responseHeadersStart();
}
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
//收到100-continue,重新尝试去读取实际的响应
response = exchange.readResponseHeaders(false)
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
exchange.responseHeadersEnd(response);
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
//连接更新了,我们需要确保拦截器能得到非空的响应体;
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
//服务器连接成功,但是没有内容~
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
我们已经知道了如何使用OkHttp进行网络请求,以及建立网络请求添加了哪些拦截器等等,但是它是怎么建立网络连接的呢?这就是这部分我们要分析的内容。首先,我们已经知道OkHttp是通过ConnectInterceptor拦截器建立与服务器之间的网络连接。下面我们就通过transmitter.newExchange()方法分析一下连接建立的逻辑。在Transmitter中newExchange()方法源码如下:
/** Returns a new exchange to carry a new request and response. */
Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
synchronized (connectionPool) {
if (noMoreExchanges) {
throw new IllegalStateException("released");
}
if (exchange != null) {
throw new IllegalStateException("cannot make a new request because the previous response "
+ "is still open: please call response.close()");
}
}
ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);
Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);
synchronized (connectionPool) {
this.exchange = result;
this.exchangeRequestDone = false;
this.exchangeResponseDone = false;
return result;
}
}
方法中的exchangeFinder就是用于查找连接关系的对象,关于为什么需要查找,这部分的逻辑需要查看ExchangeFinder中关于查找策略的相关内容。在ExchangeFinder中的find()方法中我们看到“
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
”在这里调用了findHealthyConnection()方法来获取RealConnection实例对象,我们猜测它就是真实的网络连接吧。我们继续阅读findHealthyConnection()方法,我们看到里面通过死循环去查找连接状况良好的RealConnection实例对象candidate,而关于candidate则是通过findConnection()方法查找的,下面我们就看一下findConnection()方法的源码:
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
boolean foundPooledConnection = false;
//需要查找的连接对象
RealConnection result = null;
Route selectedRoute = null;
RealConnection releasedConnection;
Socket toClose;
//获取池中的连接
synchronized (connectionPool) {
if (transmitter.isCanceled()) throw new IOException("Canceled");
hasStreamFailure = false; // This is a fresh attempt.
//尝试使用已经分配的连接;
releasedConnection = transmitter.connection;
toClose = transmitter.connection != null && transmitter.connection.noNewExchanges
? transmitter.releaseConnectionNoEvents()
: null;
if (transmitter.connection != null) {
// We had an already-allocated connection and it's good.
//已分配的连接正常
result = transmitter.connection;
releasedConnection = null;
}
//省略部分代码
...
}
closeQuietly(toClose);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
//找到连接,事件回调
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
}
if (result != null) {
// If we found an already-allocated or pooled connection, we're done.
//获取的连接已分配或者已经放入连接池了,就代表已经完成了。
return result;
}
// If we need a route selection, make one. This is a blocking operation.
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
List routes = null;
synchronized (connectionPool) {
//省略部分代码
...
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
//重新建立连接
result = new RealConnection(connectionPool, selectedRoute);
connectingConnection = result;
}
}
// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
return result;
}
// Do TCP + TLS handshakes. This is a blocking operation.
//RealConnection 进行连接尝试,是阻塞时操作;
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
connectionPool.routeDatabase.connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
connectingConnection = null;
// Last attempt at connection coalescing, which only occurs if we attempted multiple
// concurrent connections to the same host.
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {
// We lost the race! Close the connection we created and return the pooled connection.
result.noNewExchanges = true;
socket = result.socket();
result = transmitter.connection;
// It's possible for us to obtain a coalesced connection that is immediately unhealthy. In
// that case we will retry the route we just successfully connected with.
nextRouteToTry = selectedRoute;
} else {
connectionPool.put(result);
transmitter.acquireConnectionNoEvents(result);
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);
return result;
}
上述代码的连接查找或者创建策略就是:
1>如果当前的网络连接呼叫已经具有满足条件的连接,则使用该连接;
2>如果连接池中有一个可以满足的连接,则使用该连接;
3>如果没有符合条件的,则尝试重新创建新的连接;
result.connect()则是进行TCP握手的操作;下面我们就看一下在Real Connection中连接的具体逻辑:
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
EventListener eventListener) {
if (protocol != null) throw new IllegalStateException("already connected");
RouteException routeException = null;
List connectionSpecs = route.address().connectionSpecs();
ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
...
while (true) {
try {
//路由链接通道需要Http代理(HTTP proxy)
if (route.requiresTunnel()) {
//通过代理通道建立HTTPS连接去完成工作~
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
if (rawSocket == null) {//创建的socket对象不存在
// We were unable to connect the tunnel but properly closed down our resources.
break;
}
} else {
//通过原生的Socket建立一个完全的HTTP或者HTTPS连接去完成工作~
connectSocket(connectTimeout, readTimeout, call, eventListener);
}
//确定连接协议~
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
eventListener.connectEnd(call, route.socketAddress(), route.proxy(), protocol);
break;
} catch (IOException e) {
//发生异常:关闭连接,释放资源...
closeQuietly(socket);
closeQuietly(rawSocket);
socket = null;
rawSocket = null;
source = null;
sink = null;
handshake = null;
protocol = null;
http2Connection = null;
eventListener.connectFailed(call, route.socketAddress(), route.proxy(), null, e);
if (routeException == null) {
routeException = new RouteException(e);
} else {
routeException.addConnectException(e);
}
if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
throw routeException;
}
}
}
if (route.requiresTunnel() && rawSocket == null) {
ProtocolException exception = new ProtocolException("Too many tunnel connections attempted: "
+ MAX_TUNNEL_ATTEMPTS);
throw new RouteException(exception);
}
//获取当前可承载最大的连接数;
if (http2Connection != null) {
synchronized (connectionPool) {
allocationLimit = http2Connection.maxConcurrentStreams();
}
}
}
RealConnection实例对象通过调用该方法与服务器建立连接,建立连接有两种形式:一种是通过connectTunnel()方法建立连接。该方法是主要是通过connectSocket()方法建立的连接;另一种就是直接通过connectSocket()建立连接。区别是前者使用了代理通道,而后者使用了原始的socket创建的连接。
下面我们看一下connectSocket()方法的源码。
/** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
//初始化Socket对象;
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
eventListener.connectStart(call, route.socketAddress(), proxy);
rawSocket.setSoTimeout(readTimeout);
try {
//使用原生Socket建立连接;
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
//初始化数据缓冲区;
try {
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
} catch (NullPointerException npe) {
if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
throw new IOException(npe);
}
}
}
最终我们看到OkHttp是使用原生的Socket建立的连接和进行数据的通信的。
>>推广:
想要一起学习理财的程序员/程序猿们可以关注我的公众号:久财之道。
我们是努力学习股票理财知识,致力于提升生活质量的小散们。