大致的使用是這樣的:
OkHttpClient client = ...
OkHttpClient clientWith30sTimeout = client.newBuilder()
.readTimeout(30, TimeUnit.SECONDS)
.build();
Response response = clientWith30sTimeout.newCall(request).execute();
OkHttpClient是產(chǎn)生Call的工廠類,而Call是為執(zhí)行網(wǎng)絡(luò)請(qǐng)求做準(zhǔn)備的一個(gè)接口,Call可以被取消,call對(duì)象代表一個(gè)單獨(dú)的request/response對(duì)。
public interface Call {
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
interface Factory {
Call newCall(Request request);
}
}
一,究竟一個(gè)請(qǐng)求/響應(yīng)通路是怎么的呢?
OkHttpClient類有個(gè)SocketFactory的成員,這是產(chǎn)生Socket的抽象工廠類,它有5個(gè)重載的抽象方法。
public abstract SocketcreateSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException;
其4個(gè)參數(shù)含義分別是:服務(wù)端地址,服務(wù)端端口,本機(jī)地址,本機(jī)端口。這個(gè)抽象類有個(gè)默認(rèn)的實(shí)現(xiàn)類DefaultSocketFactory,它創(chuàng)建Socket方法的語(yǔ)句:
return new Socket(address, port, clientAddress, clientPort);
OkHttpClient在構(gòu)造時(shí)做了哪些事情呢?
private OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors); this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner( certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
}
包括代理,協(xié)議,攔截器,緩存,socket,連接池,dns,超時(shí),重試等一些請(qǐng)求的準(zhǔn)備工作。這里面每一項(xiàng)具體如何發(fā)揮作用的,等后面分析。
當(dāng)一個(gè)OkHttpClient構(gòu)造好之后,調(diào)用newCall(Request request)方法產(chǎn)生一個(gè)Call對(duì)象,下面看一個(gè)實(shí)現(xiàn)了Call接口的類RealCall。
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
重點(diǎn)看Response是怎么得到的。
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
}
Response是由ApplicationInterceptorChain來得到的。
@Override public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor + " returned null");
}
return interceptedResponse;
}
// No more interceptors. Do HTTP. return getResponse(request, forWebSocket);
}
到底所謂攔截器Interceptor和鏈Chain是干嘛的呢?注釋說了:
Observes, modifies, and potentially short-circuits requests going out and the corresponding* responses coming back in. Typically interceptors add, remove, or transform headers on the request* or response.
大概意思就是觀察/修改/短路請(qǐng)求發(fā)出和短路回來的響應(yīng)。通常攔截器增加/移除或轉(zhuǎn)化request/response的頭部。
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
下面是ApplicationInterceptorChain對(duì)Interceptor.Chain的proceed(Request r)方法的具體實(shí)現(xiàn):
@Override public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor + " returned null");
} return interceptedResponse;
}
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
攔截器起什么作用,這里要搞清楚,似乎很重要。
關(guān)鍵點(diǎn)在最后一句,這里返回了Response。
Response getResponse(Request request, boolean forWebSocket) throws IOException {
// Copy body metadata to the appropriate request headers.
RequestBody body = request.body();
if (body != null) {
Request.Builder requestBuilder = request.newBuilder();
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
} 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");
}
request = requestBuilder.build();
}
// Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);
int followUpCount = 0; while (true) {
if (canceled) {
engine.releaseStreamAllocation();
throw new IOException("Canceled");
}
boolean releaseConnection = true;
try {
engine.sendRequest();
engine.readResponse();
releaseConnection = false;
} catch (RequestException e) {
// The attempt to interpret the request failed. Give up.
throw e.getCause();
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
HttpEngine retryEngine = engine.recover(e.getLastConnectException(), true, null);
if (retryEngine != null) {
releaseConnection = false;
engine = retryEngine;
continue;
}
// Give up; recovery is not possible.
throw e.getLastConnectException();
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
HttpEngine retryEngine = engine.recover(e, false, null);
if (retryEngine != null) {
releaseConnection = false;
engine = retryEngine;
continue;
}
// Give up; recovery is not possible.
throw e;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
StreamAllocation streamAllocation = engine.close();
streamAllocation.release();
}
}
Response response = engine.getResponse();
Request followUp = engine.followUpRequest();
if (followUp == null) {
if (!forWebSocket) {
engine.releaseStreamAllocation();
}
return response;
}
StreamAllocation streamAllocation = engine.close();
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (!engine.sameConnection(followUp.url())) {
streamAllocation.release();
streamAllocation = null;
}
else if (streamAllocation.stream() != null) {
throw new IllegalStateException("Closing the body of " + response + " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null, response);
}
}
發(fā)現(xiàn)HTTP請(qǐng)求/響應(yīng)發(fā)生的核心,是HttpEngine這個(gè)類。
engine.sendRequest();
engine.readResponse();
大概是這兩個(gè)方法調(diào)用之后,就可以從engine中取出Response了。
Response response = engine.getResponse();
那么在HttpEngine調(diào)sendRequest()方法時(shí),發(fā)生了什么事呢?
我把這個(gè)方法的注釋摘抄一下:
Figures out what the response source will be, and opens a socket to that source if necessary. Prepares the request headers and gets ready to start writing the request body if it exists.
大意:指明response源是什么樣子,打開一個(gè)socket連接到那個(gè)response源上,準(zhǔn)備請(qǐng)求頭并做好開始寫請(qǐng)求體的準(zhǔn)備。
然后看一下readResponse()這個(gè)方法做了什么事情。
Flushes the remaining request header and body, parses the HTTP response headers and starts reading the HTTP response body if it exists.
大意是將剩余的請(qǐng)求頭和請(qǐng)求頭清除出來,然后解析HTTP響應(yīng)頭,開始讀取HTTP響應(yīng)體。
未完,待續(xù)。
下午要出去面試了。不知道又會(huì)是個(gè)什么鬼樣。