介紹
OkHttp是一個(gè)用于Android和Java應(yīng)用程序的HTTP客戶端。相關(guān)參數(shù)和配置都是用Builder模式構(gòu)建,簡(jiǎn)單使用如下。
OkHttpClient httpClient = new OkHttpClient.Builder().build();
Request request = new Request.Builder().get().url("http://www.baidu.com").build();
Call call = httpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
上面是異步請(qǐng)求方式,而同步請(qǐng)求方式只需要替換call調(diào)用的方法即可:
call.execute();
當(dāng)OkHttp發(fā)送同步請(qǐng)求后,就會(huì)進(jìn)入阻塞狀態(tài),直到收到響應(yīng)。而異步請(qǐng)求則是異步執(zhí)行的。
源碼分析
基于OkHttp3.14.0
OkHttpClient的構(gòu)建
OkHttpClient是OkHttp客戶端類(lèi),使用Builder模式構(gòu)建,初始化OkHttpClient靜態(tài)內(nèi)部類(lèi)Builder,并調(diào)用器build方法即可完成OkHttpClient的構(gòu)建。
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
在Builder構(gòu)造方法中進(jìn)行相關(guān)參數(shù)的初始化賦值, 其中dispatcher分發(fā)器是OkHttp中很重要的概念,負(fù)責(zé)調(diào)度和管理異步請(qǐng)求隊(duì)列,決定異步請(qǐng)求何時(shí)執(zhí)行的策略。
public OkHttpClient build() {
return new OkHttpClient(this);
}
buid方法將構(gòu)建的Builder對(duì)象傳入到OkHttpClient的構(gòu)造方法并返回OkHttpClient對(duì)象。
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.eventListenerFactory = builder.eventListenerFactory;
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 = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
//...
this.connectionPool = builder.connectionPool;
//...省略部分初始化復(fù)制代碼
}
Request的構(gòu)建
Request主要是OkHttp請(qǐng)求參數(shù)的封裝,也是使用Builder模式構(gòu)建,初始化Request靜態(tài)內(nèi)部類(lèi)Builder,并調(diào)用其build方法完成Request的構(gòu)建。
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
這里默認(rèn)請(qǐng)求方法設(shè)置為GET方法,并且初始化headers請(qǐng)求頭參數(shù)。
Builder可以設(shè)置網(wǎng)絡(luò)請(qǐng)求的方法,url等參數(shù)
public Builder get() {
return method("GET", null);
}
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
調(diào)用build方法,完成Request的創(chuàng)建。
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
如上,Request主要封裝了url,method,headers和請(qǐng)求體body。
Call的創(chuàng)建
Call是一個(gè)接口,其實(shí)現(xiàn)類(lèi)是RealCall。這個(gè)Call就是準(zhǔn)備好被執(zhí)行的一個(gè)請(qǐng)求,它可以被取消,但是不能被執(zhí)行兩次,因?yàn)檫@個(gè)對(duì)象代表的一個(gè)單獨(dú)的請(qǐng)求響應(yīng)對(duì)。
將request對(duì)象傳遞到OkHttpClient對(duì)象的newCall方法中進(jìn)行Call對(duì)象的創(chuàng)建
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
調(diào)用RealCall的newRealCall方法
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
該方法中直接實(shí)例化了一個(gè)RealCall對(duì)象并返回
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
this.timeout = new AsyncTimeout() {
@Override protected void timedOut() {
cancel();
}
};
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
retryAndFollowUpInterceptor重定向和重試攔截器是一個(gè)攔截器對(duì)象,這也是OkHttp中一個(gè)重要的概念,而OkHttp就是通過(guò)執(zhí)行攔截器鏈中的各個(gè)攔截器完成的網(wǎng)絡(luò)請(qǐng)求。
調(diào)用enqueue方法進(jìn)行異步網(wǎng)絡(luò)請(qǐng)求
經(jīng)過(guò)前幾步,客戶端類(lèi)、參數(shù)類(lèi),以及請(qǐng)求類(lèi)RealCall已經(jīng)完成創(chuàng)建,下面就是調(diào)用enqueue發(fā)起網(wǎng)絡(luò)請(qǐng)求并得到回調(diào)結(jié)果了。
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//...
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
由于Call只能執(zhí)行一次,如果判斷executed標(biāo)志位為true,則拋出異常,否則將executed置為true。
將傳遞進(jìn)來(lái)的responseCallback對(duì)象封裝成AsyncCall,而AsyncCall繼承自NamedRunnable,它是一個(gè)Runnable對(duì)象。AsyncCall作為參數(shù)傳遞到dispatcher的enqueue方法中
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
將call對(duì)象傳遞到readyAsyncCalls隊(duì)列,這是一個(gè)等待執(zhí)行的Call隊(duì)列。調(diào)用promoteAndExecute方法
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
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;
dispatcher中維護(hù)這兩個(gè)隊(duì)列,一個(gè)是剛看到的等待執(zhí)行call隊(duì)列readyAsyncCalls,還有一個(gè)是正在執(zhí)行call隊(duì)列runningAsyncCalls(包括正在執(zhí)行以及取消了的call)。而前面enqueue方法中,我們都將call添加到了readyAsyncCalls隊(duì)列中,所以該方法的作用是將符合條件的call從readyAsyncCalls中移除添加到runningAsyncCalls,并使用線程池執(zhí)行這些call請(qǐng)求。
首先遍歷readyAsyncCalls隊(duì)列,判斷如果當(dāng)前runningAsyncCalls隊(duì)列數(shù)量大于最大請(qǐng)求數(shù)64個(gè),直接break跳出;如果call請(qǐng)求的host存在的請(qǐng)求數(shù)大于5,則進(jìn)行下一個(gè)循環(huán)。如果上面的兩個(gè)條件都過(guò)了,就將當(dāng)前call從readyAsyncCalls中移除,并將其添加到executableCalls和runningAsyncCalls隊(duì)列中。最后遍歷executableCalls隊(duì)列,調(diào)用asyncCall的executeOn方法,在線程池中執(zhí)行請(qǐng)求
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
該方法中,主要執(zhí)行了三個(gè)操作:
1.這個(gè)try塊中執(zhí)行當(dāng)前AsyncCall
2.catch塊進(jìn)行了請(qǐng)求錯(cuò)誤回調(diào)
3.如果未執(zhí)行成功,在finally中將當(dāng)前AsyncCall從runningAsyncCalls中移除
主要看一下第一個(gè)操作。AsyncCall繼承了NamedRunnable對(duì)象,而在NamedRunnable的run方法中調(diào)用了execute抽象方法,所以主要執(zhí)行邏輯由AsyncCall的execute完成。
@Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
e = timeoutExit(e);
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
該方法執(zhí)行的操作:
1.try塊中調(diào)用getResponseWithInterceptorChain方法依次執(zhí)行攔截器鏈中的攔截器獲得response結(jié)果,判斷retryAndFollowUpInterceptor攔截器如果是取消狀態(tài),直接進(jìn)行失敗回調(diào),否則進(jìn)行成功回調(diào)。
2.catch塊也進(jìn)行了相應(yīng)的錯(cuò)誤回調(diào)。
3.同樣在finally塊中,不管當(dāng)前請(qǐng)求執(zhí)行成功與否都移除了當(dāng)前AsyncCall對(duì)象。
到這里就完成了OkHttp網(wǎng)絡(luò)請(qǐng)求的全部操作,獲取了請(qǐng)求成功的響應(yīng)并進(jìn)行了成功回調(diào),如果出錯(cuò)了,也進(jìn)行了相應(yīng)的失敗回調(diào)。
OkHttp攔截器鏈的執(zhí)行
OkHttp網(wǎng)絡(luò)請(qǐng)求的操作主要就是依次調(diào)用并執(zhí)行攔截器鏈中的攔截器完成的。
getResponseWithInterceptorChain()就是這個(gè)攔截器鏈調(diào)用流程的起點(diǎn):
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
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, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
這里將攔截器依次添加到攔截器集合中,接下來(lái)初始化了攔截器鏈對(duì)象,這個(gè)攔截器鏈持有了所有的應(yīng)用攔截器,網(wǎng)絡(luò)攔截器,OkHttp核心以及網(wǎng)絡(luò)調(diào)用者,最后調(diào)用了攔截器鏈的proceed方法
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
調(diào)用四個(gè)參數(shù)的proceed方法
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.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.httpCodec != 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, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != 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;
}
這里將index值加1,并構(gòu)建了下一個(gè)攔截器鏈對(duì)象next,然后獲取到當(dāng)前index=0的攔截器對(duì)象interceptor,調(diào)用其intercept方法執(zhí)行該攔截器邏輯,并將next對(duì)象傳入方法中。我們看一下第一個(gè)攔截器retryAndFollowUpInterceptor的intercept方法的一個(gè)關(guān)鍵邏輯
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//...
response = realChain.proceed(request, streamAllocation, null, null);
//...
}
這里獲取前面構(gòu)建的index為1的next攔截器對(duì)象,調(diào)用了它的proceed方法,又會(huì)獲取到index為1的BridgeInterceptor攔截器并調(diào)用其intercept方法,所以會(huì)依次執(zhí)行下一個(gè)攔截器的intercept的方法完成網(wǎng)絡(luò)請(qǐng)求操作。
下面依次看一下OkHttp提供的5個(gè)攔截器的主要邏輯和作用
RetryAndFollowUpInterceptor重試和重定向攔截器
該攔截器負(fù)責(zé)當(dāng)請(qǐng)求發(fā)生錯(cuò)誤時(shí)進(jìn)行錯(cuò)誤重試,以及對(duì)請(qǐng)求進(jìn)行重定向。當(dāng)請(qǐng)求取消時(shí),它會(huì)拋出IOException異常。
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
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, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// 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();
}
Request followUp;
try {
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
if (followUp == null) {
streamAllocation.release();
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
方法中首先構(gòu)建一個(gè)StreamAllocation對(duì)象,然后進(jìn)入while(true)循環(huán),如果標(biāo)志位canceled為true會(huì)直接拋出IOException異常。
調(diào)用攔截器鏈realChain的proceed方法繼續(xù)執(zhí)行下一個(gè)攔截器的邏輯,如果發(fā)生了RouteException異常,進(jìn)入第一個(gè)catch塊
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
continue;
這里判斷如果這個(gè)請(qǐng)求是不可以恢復(fù)的話,會(huì)直接拋出異常,否則continue繼續(xù)while循環(huán)執(zhí)行請(qǐng)求。當(dāng)發(fā)生IOException異常時(shí)進(jìn)入第二個(gè)catch塊,判斷邏輯和前面相同,如果可恢復(fù)的話則繼續(xù)while循環(huán),在finally中進(jìn)行了資源的釋放。上面就完成了請(qǐng)求的重試。
調(diào)用followUpRequest方法
private Request followUpRequest(Response userResponse, Route route) throws IOException {
if (userResponse == null) throw new IllegalStateException();
int responseCode = userResponse.code();
final String method = userResponse.request().method();
switch (responseCode) {
case HTTP_PROXY_AUTH:
Proxy selectedProxy = route != null
? route.proxy()
: client.proxy();
if (selectedProxy.type() != Proxy.Type.HTTP) {
throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
}
return client.proxyAuthenticator().authenticate(route, userResponse);
case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);
case HTTP_PERM_REDIRECT:
case HTTP_TEMP_REDIRECT:
// "If the 307 or 308 status code is received in response to a request other than GET
// or HEAD, the user agent MUST NOT automatically redirect the request"
if (!method.equals("GET") && !method.equals("HEAD")) {
return null;
}
// fall-through
//...省略部分代碼
case HTTP_CLIENT_TIMEOUT:
if (!client.retryOnConnectionFailure()) {
return null;
}
if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
return null;
}
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.
return null;
}
if (retryAfter(userResponse, 0) > 0) {
return null;
}
return userResponse.request();
//...省略部分代碼
default:
return null;
}
}
這個(gè)方法會(huì)配置請(qǐng)求以接收響應(yīng),它會(huì)添加請(qǐng)求的認(rèn)證頭部,對(duì)請(qǐng)求進(jìn)行重定向,或者處理請(qǐng)求超時(shí)情況。如果方法中判斷這幾種情況都不需要處理的話,會(huì)返回null,否則會(huì)返回一個(gè)request。判斷如果得到的followUp為空的話,會(huì)直接返回當(dāng)前響應(yīng)對(duì)象,跳出當(dāng)前循環(huán)。上面就完成了請(qǐng)求重定向的處理,該攔截器的操作就分析完了。
BridgeInterceptor 橋攔截器
該攔截器是應(yīng)用層代碼和網(wǎng)絡(luò)代碼的橋梁。首先它將用戶的請(qǐng)求構(gòu)建成網(wǎng)絡(luò)請(qǐng)求。然后它繼續(xù)請(qǐng)求網(wǎng)絡(luò),最后它將網(wǎng)絡(luò)響應(yīng)構(gòu)建成用戶響應(yīng)返回。下面分析intercept方法中的這幾個(gè)操作
RequestBody body = userRequest.body();
if (body != null) {
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");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
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;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
上面判斷用戶請(qǐng)求中如果沒(méi)有配置一些請(qǐng)求頭,則會(huì)補(bǔ)全配置Content-Type、Content-Length、Host和Connection這些請(qǐng)求頭。如果我們配置了可以接收gzip的壓縮數(shù)據(jù),我們也要負(fù)責(zé)解壓傳輸流。判斷獲取到的cookies不為空的話,也將Cookie添加到header中。
在配置完請(qǐng)求頭后,調(diào)用攔截器鏈的proceed方法發(fā)起請(qǐng)求獲得響應(yīng)
Response networkResponse = chain.proceed(requestBuilder.build());
得到響應(yīng),接收headers保存cookies信息,如下
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
最后判斷如果設(shè)置了可以接收gzip壓縮數(shù)據(jù)流,則會(huì)解壓壓縮流數(shù)據(jù)。最后將處理完后的響應(yīng)返回
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();
CacheInterceptor 緩存攔截器
顧名思義,這個(gè)就是處理網(wǎng)絡(luò)請(qǐng)求時(shí)的緩存的攔截器。下面分析intercept方法的邏輯
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,將請(qǐng)求request和獲取到的緩存?zhèn)魅氲紺acheStrategy的Factory中,并調(diào)用get方法獲取到緩存策略對(duì)象。緩存策略的主要作用是根據(jù)傳入的request和response判斷是使用網(wǎng)絡(luò),緩存還是兩者都要。取得緩存策略的過(guò)程會(huì)向請(qǐng)求頭添加條件,例如If-Modified-Since;或者如果緩存響應(yīng)數(shù)據(jù)是可能過(guò)期的話,會(huì)為其添加warnings。
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
如果從緩存策略獲取到的響應(yīng)為空的話,則證明緩存不可用,關(guān)閉響應(yīng)。
// 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.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
如果我們配置了不使用網(wǎng)絡(luò),只使用緩存。而此時(shí)從緩存策略中獲取到的響應(yīng)為空的話,構(gòu)建一個(gè)失敗的504的響應(yīng)返回,否則直接返回緩存響應(yīng)。
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());
}
}
跟上面的攔截器一樣,調(diào)用攔截器鏈的proceed方法繼續(xù)執(zhí)行下一個(gè)攔截器邏輯并獲取響應(yīng)。
// 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());
}
}
如果從緩存策略中獲取到的響應(yīng)不為空,再判斷網(wǎng)絡(luò)請(qǐng)求返回的code為304即服務(wù)器無(wú)修改的話,我們可以直接使用緩存響應(yīng)。
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;
如果上面的條件都不滿足的話,那么我們就是用網(wǎng)絡(luò)請(qǐng)求得到的響應(yīng),判斷響應(yīng)有body并且是可緩存的話就將這個(gè)響應(yīng)保存到緩存中。最后判斷如果這個(gè)請(qǐng)求的方法是不可以緩存的話,會(huì)將其從緩存中移除,這樣緩存攔截器的主流程就走完了。
ConnectInterceptor 連接攔截器
連接攔截器打開(kāi)一個(gè)到目標(biāo)服務(wù)器的連接,并且調(diào)用下一個(gè)攔截器。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
獲取到重試和重定向攔截器中初始化streamAllocation對(duì)象,調(diào)用newStream方法
public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
//...省略部分代碼
try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
調(diào)用findHealthyConnection
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
boolean doExtensiveHealthChecks) throws IOException {
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;
}
}
// Do a (potentially slow) check to confirm that the pooled connection is still good. If it
// isn't, take it out of the pool and start again.
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}
return candidate;
}
}
這個(gè)方法會(huì)一直執(zhí)行while循環(huán)直接到找個(gè)一個(gè)健康的連接。調(diào)用findConnection方法,該方法中的方法里邏輯較多,分步來(lái)看
releasedConnection = this.connection;
toClose = releaseIfNoNewStreams();
if (this.connection != null) {
// We had an already-allocated connection and it's good.
result = this.connection;
releasedConnection = null;
}
if (!reportedAcquired) {
// If the connection was never reported acquired, don't report it as released!
releasedConnection = null;
}
首先嘗試使用已經(jīng)分配過(guò)的connection連接,如果connection不為空,即我們可以使用它。
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;
}
}
}
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;
}
如果connection為空,會(huì)試圖從連接池中獲取,如果connection不為空將其賦值給result。到這里如果我們找到了已經(jīng)分配的connection或者是連接池中的connection,就將connection返回。
//...
result = new RealConnection(connectionPool, selectedRoute);
//...
如果上面都沒(méi)找到可用的connection,這里實(shí)例化了一個(gè)新的connection賦值給result。
// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
調(diào)用了result的connect方法建立了連接,其內(nèi)部是建立的socket連接,內(nèi)部具體細(xì)節(jié)暫時(shí)不深究。最后調(diào)用connection的newCodec方法獲取了HttpCodec對(duì)象,HttpCodec對(duì)象封裝了okio的輸入和輸出流對(duì)象。
將request和streamAllocation以及這步獲得的connection、httpCodec對(duì)象傳入到攔截器鏈的proceed方法調(diào)用下一個(gè)攔截器。
CallServerInterceptor 請(qǐng)求服務(wù)攔截器
這是攔截器鏈中的最后一個(gè)攔截器,它負(fù)責(zé)向服務(wù)器發(fā)送網(wǎng)絡(luò)請(qǐng)求。
看一下它interceptor方法具體發(fā)送網(wǎng)絡(luò)請(qǐng)求的流程:
httpCodec.writeRequestHeaders(request);
這里準(zhǔn)備請(qǐng)求頭,并將其寫(xiě)入到流中發(fā)送到服務(wù)器。
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.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!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.
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
這里判斷如果請(qǐng)求頭中有Expect: 100-continue信息,則請(qǐng)求時(shí)會(huì)先不發(fā)送請(qǐng)求體信息,直到接收到服務(wù)器的100-continue應(yīng)答后才會(huì)將數(shù)據(jù)post給服務(wù)器。
Expect: 100-continue解釋?zhuān)嚎蛻舳讼劝l(fā)送了一個(gè)請(qǐng)求,這個(gè)請(qǐng)求的header中包含了一個(gè)屬性
expect: 100-continue。這種情況一般出現(xiàn)于上傳大容量body或者是需要驗(yàn)證的時(shí)候。這時(shí)服務(wù)器會(huì)讀取請(qǐng)求的header并返回一個(gè)100 continue的響應(yīng),如果服務(wù)器可以提供這項(xiàng)服務(wù)的話??蛻舳嗽賹ttp請(qǐng)求發(fā)送回去。然后服務(wù)器會(huì)讀取請(qǐng)求的body并且在成功后返回200狀態(tài)碼。這樣做可以避免帶寬浪費(fèi)。
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
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
如果我們沒(méi)有指定Expect: 100-continue請(qǐng)求,服務(wù)器也返回了100-continue響應(yīng)的話,會(huì)再次嘗試獲取的真實(shí)的響應(yīng)。
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(httpCodec.openResponseBody(response))
.build();
}
如果收到了101響應(yīng),即服務(wù)端理解了客戶端的請(qǐng)求,并通知客戶端采取不同的協(xié)議,我們會(huì)重新構(gòu)建response以確保攔截器收到是非空的響應(yīng),否則直接拿到響應(yīng)。
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
當(dāng)請(qǐng)求的頭部字段或者響應(yīng)的頭部字段Connection為close時(shí),則會(huì)斷開(kāi)連接,并且會(huì)禁止connection產(chǎn)生新的流。
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
這里狀態(tài)碼表示服務(wù)器接收的請(qǐng)求已成功處理,但在請(qǐng)求報(bào)文中不含實(shí)體的主體部分。另外也不允許返回任何實(shí)體的主體。
這里判斷如果響應(yīng)碼是204或者205,但是響應(yīng)報(bào)文包含實(shí)體部分時(shí)就會(huì)拋出異常,否則將得到的response返回。
最后隨著OkHttp攔截器鏈中攔截器的依次執(zhí)行,OkHttp網(wǎng)絡(luò)請(qǐng)求就結(jié)束了。