文章目錄
- 0.簡介
- 1.從demo說起
- 2. OkHttpClient源碼解析
- 4.Call源碼解析
- 5.發起請求-同步get請求
- 6.發起請求-異步get請求
- 1)如何發起請求?
- 2)okhttp3.Dispatcher#enqueue
- 3)okhttp3.Dispatcher#promoteAndExecute
- 4)executorService是個啥?
- 5)okhttp3.RealCall.AsyncCall#executeOn
- 6)okhttp3.RealCall.AsyncCall#execute
- 7)okhttp3.Dispatcher屬性
- 7 okhttp3.RealCall#getResponseWithInterceptorChain
- 8.OkHttp主流程
- 9. okhttp3.Interceptor接口
- 10 自定義攔截器
- 11 okhttp3.internal.http.RetryAndFollowUpInterceptor攔截器
- 12.okhttp3.internal.cache.CacheInterceptor
- 13.okhttp3.internal.connection.ConnectInterceptor
- 14 okhttp3.internal.http.CallServerInterceptor
- 總結
- 附件
0.簡介
概括起來說OkHttp是一款優秀的HTTP框架,它支持get請求和post請求,支持基於Http的文件上傳和下載,支持加載圖片,支持下載文件透明的GZIP壓縮,支持響應緩存避免重複的網絡請求,支持使用連接池來降低響應延遲問題。
特點如下:
- 基於socket,可以對http進行更完全的封裝
- 支持HTTP/2,允許所有同一個主機地址的請求共享同一個socket連接
- 連接池,減少請求延時
- 透明的GZIP壓縮,減少響應數據的大小
- 緩存響應內容,避免一些完全重複的請求
- 當網絡出現問題的時候OkHttp依然堅守自己的職責,它會自動恢復一般的連接問題,如果你的服務有多個IP地址,當第一個IP請求失敗時,OkHttp會交替嘗試你配置的其他IP,OkHttp使用現代TLS技術(SNI, ALPN)初始化新的連接,當握手失敗時會回退到TLS 1.0。
1.從demo說起
先要說一下,是怎麼使用的
package com.meituan.okhttp.client;
import com.meituan.okhttp.client.interceptor.LoggingInterceptor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.BufferedSink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
@Slf4j
public class OkHttpRequest {
private static final String URL = "http://www.baidu.com";
private static final MediaType MEDIA_TYPE_JSON = MediaType
.parse("application/json; charset = utf-8");
//OkHttpClient client = new OkHttpClient();
OkHttpClient client = new OkHttpClient.Builder().build();
public void syncGet() {//同步get方法
Request request = new Request.Builder().url(URL).build();
final Call call = client.newCall(request);
//因爲同步阻塞會阻塞主方法的運行,所以另起線程,當然也可以用線程池,還不如用異步get方法
new Thread(new Runnable() {
public void run() {
try {
Response response = call.execute();//調用call拿到返回結果
log.info("syncGet: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public void aSyncGet() {//異步get方法
Request request = new Request.Builder()
.url(URL)
.get()//默認是get 可以不寫
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {//異步發起的請求會被加入到隊列中通過線程池來執行。通過回調方式拿到結果
public void onFailure(Call call, IOException e) {
log.info("onFailure");
}
public void onResponse(Call call, Response response)
throws IOException {
Request request1 = call.request();//這個只是說明可以拿到request。
log.info("request: " + request1);
log.info("aSyncGet onSuccess: " + response.body().string());
}
});
}
public void aSyncPost() {//異步post方法
RequestBody requestBody = new RequestBody() {
@Nullable
@Override
public MediaType contentType() {
return MediaType
.parse("application/json; charset = utf-8");
}
@Override
public void writeTo(@NotNull BufferedSink bufferedSink)
throws IOException {
bufferedSink.write("zcz".getBytes());
}
};
Request request = new Request.Builder()
.url(URL)
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call,
@NotNull IOException e) {
log.info("onFailure: post");
}
@Override
public void onResponse(@NotNull Call call,
@NotNull Response response) throws IOException {
log.info("onSuccess: post" + response.body().string());
}
});
}
public void testInterceptor() {//添加自定義攔截器
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();
Request request = new Request.Builder()
.url("http://www.publicobject.com/helloworld.txt")
.header("User-Agent", "OkHttp Example")
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
log.info("onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
ResponseBody body = response.body();
if (body != null) {
log.info("onResponse: " + response.body().string());
body.close();
}
}
});
}
}
這裏get方法的同步和異步只體現在Call這個對象上,只是調用的方法不同,具體有什麼不同呢?這個以後再說。還有一個需要注意的地方就是異步方法通過回調方式拿到Response。
2. OkHttpClient源碼解析
1)有兩種方式創建OkHttpClient:
OkHttpClient client = new OkHttpClient();
OkHttpClient client = new OkHttpClient.Builder().build();
其實推薦讓 OkHttpClient 保持單例,用同一個 OkHttpClient 實例來執行你的所有請求,因爲每一個 OkHttpClient 實例都擁有自己的連接池和線程池,重用這些資源可以減少延時和節省資源,如果爲每個請求創建一個 OkHttpClient 實例,顯然就是一種資源的浪費。
第一種沒什麼說的(屬性保持默認)。
說一下第二種,看到這個你會想到什麼呢?沒錯!就是builder(建造者)模式,那麼問題來了,
2)爲什麼要用建造者模式?
首先要了解建造者模式。設想一個場景:當一個類有很多屬性要初始化,並且大多都保持默認,只需要變動其中的某個或者某幾個,如果普通方法需要創建很多的重載的構造方法。建造者模式就是專門來解決這個痛點的。那麼怎麼解決的呢?
一般情況下,有一個內部類通常叫做Builder,其屬性與外部類相同,內部類的的“set方法”,除了set屬性之外,還會返回本身。這樣做的目的就是可以“鏈式”設置屬性。外部類的構造方法就可以只傳入這個內部類(外部類.屬性=內部類.屬性),而內部類的builder()方法對外部類進行初始化(調用外部類的構造方法),並返回外部類。沒錯!OkHttpClient就是使用了這種方式,理解了這個,就好理解OkHttpClient這個類了
放源代碼太麻煩,放一張結構圖,就是上面所述的那樣。
3)看看有哪些屬性
okhttp3.OkHttpClient:
final Dispatcher dispatcher; // 調度器
final @Nullable
Proxy proxy; // 代理
final List<Protocol> protocols; // 協議
final List<ConnectionSpec> connectionSpecs; // 傳輸層版本和連接協議
final List<Interceptor> interceptors; // 攔截器
final List<Interceptor> networkInterceptors; // 網絡攔截器
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector; // 代理選擇器
final CookieJar cookieJar; // cookie
final @Nullable
Cache cache; // 緩存
final @Nullable
InternalCache internalCache; // 內部緩存
final SocketFactory socketFactory; // socket 工廠
final SSLSocketFactory sslSocketFactory; // 安全套接層 socket 工廠,用於 https
final CertificateChainCleaner certificateChainCleaner; // 驗證確認響應證書 適用 HTTPS 請求連接的主機名
final HostnameVerifier hostnameVerifier; // 主機名字驗證
final CertificatePinner certificatePinner; // 證書鏈
final Authenticator proxyAuthenticator; // 代理身份驗證
final Authenticator authenticator; // 本地身份驗證
final ConnectionPool connectionPool; // 連接池
final Dns dns; // 域名
final boolean followSslRedirects; // 安全套接層重定向
final boolean followRedirects; // 本地重定向
final boolean retryOnConnectionFailure; // 重試連接失敗
final int callTimeout;
final int connectTimeout;
final int readTimeout;
final int writeTimeout;
final int pingInterval;
沒錯真的有很多屬性!屬性都有註釋,不一一解釋,感興趣的可以細看,注意的是緩存,攔截器,鏈接池等字眼,以後再解釋!
4)看看Builder這個內部類
okhttp3.OkHttpClient.Builder
public static final class Builder {
Dispatcher dispatcher;//任務調度器
@Nullable
Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
@Nullable
Cache cache;
@Nullable
InternalCache internalCache;
SocketFactory socketFactory;
@Nullable
SSLSocketFactory sslSocketFactory;
@Nullable
CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int callTimeout;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
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;
}
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null)
throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}
屬性一一對應。設值返回this,就像之前所講的。
5)再看看OkHttpClient構造方法
okhttp3.OkHttpClient#OkHttpClient
public OkHttpClient() {
this(new Builder());
}
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.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.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException(
"Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException(
"Null network interceptor: " + networkInterceptors);
}
}
這個類到此爲止,感興趣可以自己看。
3.Request源碼解析
由於也使用了建造者模式,細節就不分析了,只分析屬性,大部分可以見名知意,難理解的已經標註
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable
RequestBody body;
//通過tags來同時取消多個請求。當你構建一請求時,使用RequestBuilder.tag(tag)來分配一個標籤。
// 之後你就可以用OkHttpClient.cancel(tag)來取消所有帶有這個tag的call。
final Map<Class<?>, Object> tags;
//相當於請求頭中的Cache-Control:
private volatile @Nullable CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;//方法,默認get
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
- 設置url。可以是String類型、URL類型和HttpUrl類型。最終都是用到HttpUrl類型。
- 設置method,包含get、post方法等。默認的是get方法。post方法要傳RequestBody,類似的還有delete、put、patch。
- 設置header,方法有addHeader(String name, String value)、 removeHeader(String name)、header(String name, String value)、headers(Headers headers)。headers(Headers,headers)調用之後其它的header都會被移除,只添加這一個header。而header(String name, String value)方法調用之後,其它與這個name同名的header都會被移除,只保留這一個header。
- 設置tag,設置tag可以用來取消這一請求。如果未指定tag或者tag爲null,那麼這個request本身就會當做是一個tag用來被取消請求。
- 設置cacheControl,這個是設置到請求頭中。用來替換其它name是"Cache-Control"的header。如果cacheControl是空的話就會移除請求頭中name是"Cache-Control"的header。
4.Call源碼解析
分析完OkHttpClient和Request,繼續看demo
1)首先看看是怎麼用的
Call call = client.newCall(request);
每一個請求任務都封裝爲一個Call,其實現爲RealCall。
2)這個newCall方法來自於哪?
okhttp3.Call.Factory#newCall
public interface Call extends Cloneable {
...
interface Factory {
Call newCall(Request request);
}
}
3)OkHttpClient是怎麼調用的?
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
....
@Override
public Call newCall(Request request) {//實現接口,重寫方法
return RealCall.newRealCall(this, request, false /* for web socket */);//默認實現
}
}
其實是調用okhttp3.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;
}
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
//創建了一個RetryAndFollowUpInterceptor,用於處理請求錯誤和重定向等,
// 這是 Okhttp 框架的精髓 interceptor chain 中的一環,
// 默認情況下也是第一個攔截器,除非調用 OkHttpClient.Builder#addInterceptor(Interceptor) 來添加全局的攔截器。
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
this.timeout = new AsyncTimeout() {
@Override
protected void timedOut() {
cancel();
}
};
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
4)EventListener源碼
創建call的時候添加了一個事件監聽器,看看源碼是做什麼的,這裏包含了很多方法。其實可以理解爲"call"的生命週期。不仔細看了。
在OkHttpClient中,我們可以傳入EventListener的工廠方法,爲每一個請求創建一個EventListener,來接收非常細的事件回調
5.發起請求-同步get請求
1)如何發起請求?
看demo
Response response = call.execute();//調用call拿到返回結果
之前分析過,這裏的call其實是RealCall,直接看其execute()方法,okhttp3.RealCall#execute
/**
* 檢測這個 call 是否已經執行了,保證每個 call 只能執行一次。
* 通知 dispatcher 已經進入執行狀態,將 call 加入到 runningSyncCalls 隊列中。
* 調用 getResponseWithInterceptorChain() 函數獲取 HTTP 返回結果。
* 最後還要通知 dispatcher 自己已經執行完畢,將 call 從 runningSyncCalls 隊列中移除。
* @return
* @throws IOException
*/
@Override
public Response execute() throws IOException {//模板方法實現
synchronized (this) {
// 每個 call 只能執行一次
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
// 請求開始, 將自己加入到runningSyncCalls隊列中
client.dispatcher().executed(this);
// 通過一系列攔截器請求處理和響應處理得到最終的返回結果
Response result = getResponseWithInterceptorChain();//調用 getResponseWithInterceptorChain()獲得響應內容
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
// 請求完成, 將其從runningSyncCalls隊列中移除
client.dispatcher().finished(this);
}
}
2)okhttp3.Dispatcher#executed
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
所以其實這裏只是加入到一個隊列裏面,並沒有發起請求,發起請求還是通過getResponseWithInterceptorChain()實現的。這裏先賣一下關子,一會再講這個方法,先講一下異步的方式,因爲都會調用這個方法。
6.發起請求-異步get請求
1)如何發起請求?
看demo
call.enqueue(new Callback() {//異步發起的請求會被加入到隊列中通過線程池來執行。通過回調方式拿到結果
public void onFailure(Call call, IOException e) {
log.info("onFailure");
}
public void onResponse(Call call, Response response)
throws IOException {
Request request1 = call.request();//這個只是說明可以拿到request。
log.info("request: " + request1);
log.info("aSyncGet onSuccess: " + response.body().string());
}
});
在異步請求中,我們通過Callback來獲得簡單清晰的請求回調(onFailure、onResponse)
其實是調用okhttp3.RealCall#enqueue這個方法實現的。
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");//每個請求只能執行一次
executed = true;
}
captureCallStackTrace();//爲retryAndFollowUpInterceptor加入了一個用於追蹤堆棧信息的callStackTrace
eventListener.callStart(this);//之前加的listener,在這裏調用
//這裏創建了一個 AsyncCall 並將Callback傳入,接着再交給任務分發器 Dispatcher 來進一步處理。
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
2)okhttp3.Dispatcher#enqueue
void enqueue(AsyncCall call) {
// 將AsyncCall加入到準備異步調用的隊列中
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
3)okhttp3.Dispatcher#promoteAndExecute
/**
* Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
* them on the executor service. Must not be called with synchronization because executing calls
* can call into user code.
* <p>
* 將 readyAsyncCalls 中的任務移動到 runningAsyncCalls中,並交給線程池來執行。
* <p>
* 從準備異步請求的隊列中取出可以執行的請求(正在運行的異步請求不得超過64,同一個host下的異步請求不得超過5個),
* 加入到 executableCalls 列表中。
* 循環 executableCalls 取出請求 AsyncCall 對象,調用其 executeOn 方法。
*
* @return true if the dispatcher is currently running calls.
*/
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
//若條件允許,將readyAsyncCalls中的任務移動到runningAsyncCalls中,並交給線程池執行
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.大於最大請求數64,跳出
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 中的 runningAsyncCalls雙端隊列中通過線程池來執行。
4)executorService是個啥?
okhttp3.Dispatcher#executorService
/**
* 這個線程池沒有核心線程,線程數量沒有限制,空閒60s就會回收,適用於大量耗時較短的任務;
* 與 Executors.newCachedThreadPool() 比較類似;
* <p>
* 雖然線程池無任務上限,但是Dispatcher對入口enqueue()進行了把關,
* 最大的異步任務數默認是64,同一個主機默認是5,當然這兩個默認值是可以修改的,Dispatcher提供的修改接口;
*/
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
5)okhttp3.RealCall.AsyncCall#executeOn
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);//這裏的this指AsyncCall
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!
}
}
}
AsyncCall必然實現Runable接口,因此反過來看其run()方法,看引用(okhttp3.RealCall.AsyncCall#executeOn)知道AsyncCall其實是RealCall的一個內部類
現實是AsyncCall並沒有直接實現Runable接口
final class AsyncCall extends NamedRunnable {
...
}
但是其父類NamedRunnable實現了Runnable接口
/**
* Runnable implementation which always sets its thread name.
*/
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override
public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);//設置名字,方便監控
try {
execute();//模板方法,具體實現留給子類實現
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
這是一個模板方法的典型實現,因此需要看子類的execute()方法
6)okhttp3.RealCall.AsyncCall#execute
@Override
protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
//異步和同步走的是同樣的方式,只不過在子線程中執行
// 請求網絡獲取結果
Response response = getResponseWithInterceptorChain();//調用 getResponseWithInterceptorChain()獲得響應內容
signalledCallback = true;//這個標記主要是避免異常時2次回調
responseCallback.onResponse(RealCall.this, response);//回調Callback,將響應內容傳回去
} 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); //回調Callback告知失敗
}
} catch (Throwable t) {
cancel();
if (!signalledCallback) {
IOException canceledException = new IOException("canceled due to " + t);
responseCallback.onFailure(RealCall.this, canceledException);
}
throw t;
} finally {
//不管請求成功與否,都進行finished()操作
// 調度完成,移出隊列
client.dispatcher().finished(this);
}
}
好了,同步和異步都要調用okhttp3.RealCall#getResponseWithInterceptorChain這個方法獲取Response。
之前說了很多隊列,這裏總結一下,其實是三個隊列,都是在okhttp3.Dispatcher類裏面
7)okhttp3.Dispatcher屬性
public final class Dispatcher {
private int maxRequests = 64;//最大請求數量
private int maxRequestsPerHost = 5;//每臺主機最大的請求數量
private @Nullable Runnable idleCallback;
/**
* Executes calls. Created lazily.
*/
private @Nullable
ExecutorService executorService;//線程池,跟CachedThreadPool非常類似,這種類型的線程池,適用於大量的耗時較短的異步任務。
//異步請求,Dispatcher 是通過啓動 ExcuteService 執行,線程池的最大併發量 64,
// 異步請求先放置在 readyAsyncCalls,可以執行時放到 runningAsyncCalls 中,執行結束從runningAsyncCalls 中移除。
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//準備執行的請求隊列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在運行的請求隊列
//同步請求,由於它是即時運行的, Dispatcher 只需要運行前請求前存儲到 runningSyncCalls,
// 請求結束後從 runningSyncCalls 中移除即可。
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//一個正在運行的同步請求隊列
...
}
7 okhttp3.RealCall#getResponseWithInterceptorChain
/**
* 這裏先是創建了一個 Interceptor 的ArrayLIst,然後將各類 interceptor 全部加入到ArrayLIst中,之後按照順序執行。包含以下 interceptor:
*
* interceptors:配置 OkHttpClient 時設置的 inteceptors
* RetryAndFollowUpInterceptor:負責失敗重試以及重定向
* BridgeInterceptor:負責把用戶構造的請求轉換爲發送到服務器的請求、把服務器返回的響應轉換爲用戶友好的響應
* CacheInterceptor:負責讀取緩存直接返回、更新緩存
* ConnectInterceptor:負責和服務器建立連接
* networkInterceptors:配置 OkHttpClient 時設置的 networkInterceptors
* CallServerInterceptor:負責向服務器發送請求數據、從服務器讀取響應數據
* @return
* @throws IOException
*/
//攔截器鏈
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();//這是一個List,是有序的
interceptors.addAll(client.interceptors());//首先添加的是用戶添加的全局攔截器
interceptors.add(retryAndFollowUpInterceptor);//錯誤、重定向攔截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));//封裝request和response攔截器,橋接攔截器,橋接應用層與網絡層,添加必要的頭、
//構造這個攔截器的時候傳入的是我們構造的OkHttpClient中設置的interanlCache,
// 而當我們用默認方式構造OkHttpClient的時候是不會創建緩存的,也就是internalCache=null的
interceptors.add(new CacheInterceptor(client.internalCache()));//緩存處理,Last-Modified、ETag、DiskLruCache等
interceptors.add(new ConnectInterceptor(client));//負責和服務器建立連接攔截器
//從這就知道,通過okHttpClient.Builder#addNetworkInterceptor()傳進來的攔截器只對非網頁的請求生效
//配置 OkHttpClient 時設置的 networkInterceptors
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
//真正訪問服務器的攔截器
//負責向服務器發送請求數據、從服務器讀取響應數據(實際網絡請求)
interceptors.add(new CallServerInterceptor(forWebSocket));
//注意這裏的0,即從頭開始執行,這裏傳入的參數很多都是null,之後再創建
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//最後通過RealInterceptorChain#proceed(Request)來執行整個 interceptor chain
Response response = chain.proceed(originalRequest);
if (retryAndFollowUpInterceptor.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
}
OkHttp的攔截器鏈可謂是其整個框架的精髓,用戶可傳入的 interceptor 分爲兩類:
①一類是全局的 interceptor,該類 interceptor 在整個攔截器鏈中最早被調用,通過 OkHttpClient.Builder#addInterceptor(Interceptor) 傳入;
②另外一類是非網頁請求的 interceptor ,這類攔截器只會在非網頁請求中被調用,並且是在組裝完請求之後,真正發起網絡請求前被調用,所有的 interceptor 被保存在 List interceptors 集合中,按照添加順序來逐個調用,具體可參考 RealCall#getResponseWithInterceptorChain() 方法。通過 OkHttpClient.Builder#addNetworkInterceptor(Interceptor) 傳入;
這裏舉一個簡單的例子,例如有這樣一個需求,我要監控App通過 OkHttp 發出的所有原始請求,以及整個請求所耗費的時間,針對這樣的需求就可以使用第一類全局的 interceptor 在攔截器鏈頭去做。
在OkHttp3中,其靈活性很大程度上體現在可以 intercept 其任意一個環節,而這個優勢便是okhttp3整個請求響應架構體系的精髓所在,先放出一張主框架請求流程圖,接着再分析源碼。
8.OkHttp主流程
簡述 OkHttp 的請求流程:
-
OkHttpClient 實現了 Call.Fctory,負責爲 Request 創建 Call。
-
RealCall 是 Call 的具體實現,它的異步請求是通過 Dispatcher 調度器利用ExcutorService實現,而最終進行網絡請求時和同步請求一樣,都是通過 getResponseWithInterceptorChain 方法實現。
-
getResponseWithInterceptorChain 方法中採用了責任鏈模式,每一個攔截器各司其職,主要做兩件事。
攔截上一層攔截器封裝好的 Request,然後自身對這個 Request 進行處理,處理後向下傳遞。
接收下一層攔截器傳遞回來的 Response,然後自身對 Response 進行處理,返回給上一層。
1)各攔截器作用
retryAndFollowUpInterceptor——失敗和重定向過濾器BridgeInterceptor——封裝request和response過濾器CacheInterceptor——緩存相關的過濾器,負責讀取緩存直接返回、更新緩存ConnectInterceptor——負責和服務器建立連接,連接池等networkInterceptors——配置 OkHttpClient 時設置的 networkInterceptorsCallServerInterceptor——負責向服務器發送請求數據、從服務器讀取響應數據(實際網絡請求)
2)攔截器鏈設計好處
高內聚,低耦合,可擴展,單一職責。
9. okhttp3.Interceptor接口
/**
* 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.
*/
public interface Interceptor {
Response intercept(Chain chain) throws IOException;//主要方法
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable
Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
各個攔截器都實現了這個接口,如果想自己擴展也可以實現該接口比如
10 自定義攔截器
com.meituan.okhttp.client.interceptor.LoggingInterceptor
package com.meituan.okhttp.client.interceptor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
@Slf4j
public class LoggingInterceptor implements Interceptor {
private static final String TAG = "LoggingInterceptor";
// private static final Logger logger = LogManager.getLogger();
@NotNull
@Override
public Response intercept(@NotNull Chain chain)
throws IOException {
Request request = chain.request();
long startTime = System.nanoTime();
Response response = chain.proceed(request);
log.info("time" + (System.nanoTime()-startTime));
return response;
}
}
com.meituan.okhttp.client.OkHttpRequest#testInterceptor
public void testInterceptor() {//添加自定義攔截器
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();
Request request = new Request.Builder()
.url("http://www.publicobject.com/helloworld.txt")
.header("User-Agent", "OkHttp Example")
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
log.info("onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
ResponseBody body = response.body();
if (body != null) {
log.info("onResponse: " + response.body().string());
body.close();
}
}
});
}
11 okhttp3.internal.http.RetryAndFollowUpInterceptor攔截器
其interceptor方法很長,但是簡化以後
1)簡化代碼
@Override public Response intercept(Chain chain) throws IOException {
。。。
while (true) {
。。。
try {
response = realChain.proceed(request, streamAllocation, null, null);
}
。。。
if(滿足條件){
return response;
}
。。。
//不滿足條件,一頓操作,賦值再來!
request = followUp;
priorResponse = response;
}
}
其實就流程來上,我認爲宏觀上代碼縮減到這樣就夠了,甚至可以再刪點,這裏先從流程上理解.其實代碼成這樣,基本上大家都能理解了,一個while(true)表明這是個循環體,循環體主要做的事可以看到其實是遞歸的主要方法。
response = realChain.proceed(request, streamAllocation, null, null);
執行了這個方法後,就會交給下一個過濾器繼續執行,所以單從這裏來看,我們可以簡單的理解爲這個過濾器其實沒做什麼。但是當出現了一些問題,導致不滿足條件的時候,就需要進行一系列的操作,重新複製Request,重新請求,這也就是while的功能,對應的也就是這個過濾器的主要功能:重試和重定向。這裏我們宏觀上已經對RetryAndFollowUpInterceptor有了一個基本的理解了。
2)過程細節
@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,剛傳入的時候是null,這個類大概可以理解爲是處理Connections,Streams,Calls三者之間的關係
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
//統計重定向次數,不能大於20
int followUpCount = 0;
Response priorResponse = null;
while (true) {
//取消
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;//初始化時賦值爲true
try {
//調用下一個interceptor的來獲得響應內容
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.
//沒有捕獲到的異常,最終要釋放
//由於releaseConnection初始化爲true,
// 而當正常執行realChain.proceed或在執行過程中捕捉到異常時設置爲false,
// 所以當執行過程中捕捉到沒有檢測到的異常時,需要釋放一些內容。
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
//這裏基本上都沒有講,priorResponse是用來保存前一個Resposne的,
// 這裏可以看到將前一個Response和當前的Resposne結合在一起了,
// 對應的場景是,當獲得Resposne後,發現需要重定向,則將當前Resposne設置給priorResponse,再執行一遍流程,
//直到不需要重定向了,則將priorResponse和Resposne結合起來。
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp;
try {
//重定向處理
//判斷是否需要重定向,如果需要重定向則返回一個重定向的Request,沒有則爲null
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
if (followUp == null) {//不需要重定向
//是WebSocket,釋放
streamAllocation.release();
return response;//返回response
}
//需要重定向,關閉響應流
closeQuietly(response.body());
//重定向次數++,並且小於最大重定向次數MAX_FOLLOW_UPS(20)
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
//是UnrepeatableRequestBody, 剛纔看過也就是是流類型,沒有被緩存,不能重定向
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
//判斷是否相同,不然重新創建一個streamConnection
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;
}
}
3)okhttp3.internal.http.RetryAndFollowUpInterceptor#recover
/**
* Report and attempt to recover from a failure to communicate with a server. Returns true if
* {@code e} is recoverable, or false if the failure is permanent. Requests with a body can only
* be recovered if the body is buffered or if the failure occurred before the request has been
* sent.
*/
private boolean recover(IOException e, StreamAllocation streamAllocation,
boolean requestSendStarted, Request userRequest) {
streamAllocation.streamFailed(e);
// The application layer has forbidden retries.
//如果OkHttpClient直接配置拒絕失敗重連,return false,默認時true
if (!client.retryOnConnectionFailure()) return false;
// We can't send the request body again.
//如果請求已經發送,並且這個請求體是一個UnrepeatableRequestBody類型,則不能重試。
//StreamedRequestBody實現了UnrepeatableRequestBody接口,是個流類型,不會被緩存,所以只能執行一次,具體可看。
if (requestSendStarted && requestIsUnrepeatable(e, userRequest)) return false;
// This exception is fatal.
//一些嚴重的問題,就不要重試了
if (!isRecoverable(e, requestSendStarted)) return false;
// No more routes to attempt.
//沒有更多的路由就不要重試了
if (!streamAllocation.hasMoreRoutes()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
12.okhttp3.internal.cache.CacheInterceptor
/**
* 1.通過Request嘗試到Cache中拿緩存(裏面非常多流程),當然前提是OkHttpClient中配置了緩存,默認是不支持的。
* 2.根據response,time,request創建一個緩存策略,用於判斷怎樣使用緩存。
* 3.如果緩存策略中設置禁止使用網絡,並且緩存又爲空,則構建一個Resposne直接返回,注意返回碼=504
* 4.緩存策略中設置不使用網絡,但是又緩存,直接返回緩存
* 5.接着走後續過濾器的流程,chain.proceed(networkRequest)
* 6.當緩存存在的時候,如果網絡返回的Resposne爲304,則使用緩存的Resposne。
* 7.構建網絡請求的Resposne
* 8.當在OKHttpClient中配置了緩存,則將這個Resposne緩存起來。
* 9.緩存起來的步驟也是先緩存header,再緩存body。
* 10.返回Resposne。
*
*
* @param chain
* @return
* @throws IOException
*/
@Override
public Response intercept(Chain chain) throws IOException {
//1.嘗試通過這個Request拿緩存
//默認cache爲null,可以配置cache,不爲空嘗試獲取緩存中的response
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//根據response,time,request創建一個緩存策略,用於判斷怎樣使用緩存
//對應的是CacheStrategy這個類,裏面主要涉及Http協議中緩存的相關設置
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
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.
//2.如果不允許使用網絡並且緩存爲空,新建一個504的Resposne返回。
//如果緩存策略中禁止使用網絡,並且緩存又爲空,則構建一個Resposne直接返回,注意返回碼=504
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();
}
Response networkResponse = null;
try {
//調用下一個攔截器進行網絡請求
//3.如果不允許使用網絡,但是有緩存,返回緩存。
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.
//4.鏈式調用下一個過濾器。
networkResponse = chain.proceed(networkRequest);
//當緩存響應和網絡響應同時存在的時候,選擇用哪個
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
//如果返回碼是304,客戶端有緩衝的文檔併發出了一個條件性的請求(一般是提供If-Modified-Since頭表示客戶
// 只想比指定日期更新的文檔)。服務器告訴客戶,原來緩衝的文檔還可以繼續使用。
//則使用緩存的響應
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());
}
}
//使用網絡響應
//6、7.使用網絡請求得到的Resposne,並且將這個Resposne緩存起來(前提當然是能緩存)。
// 接下來就是腦袋都大的細節了,我也不敢說分析的十分詳細,只能就我的理解總體分析,學習。
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//所以默認創建的OkHttpClient是沒有緩存的
//存緩存這個步驟的分析了,其實前面的取緩存的分析結束後,
// 這裏對存緩存不難猜測其實是想對應的,也就比較好理解了,對應的大體應該是header存入clean[0],body存入clean[1]。
if (cache != null) {
//9。 將響應緩存
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
//緩存Resposne的Header信息
CacheRequest cacheRequest = cache.put(response);
//緩存body
return cacheWritingResponse(cacheRequest, response);
}
//只能緩存GET....不然移除request
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
13.okhttp3.internal.connection.ConnectInterceptor
@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 httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
//獲取連接
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
14 okhttp3.internal.http.CallServerInterceptor
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
//開始寫入header
realChain.eventListener().requestHeadersStart(realChain.call());
//HttpCodec其實是一個接口,對應的使用策略模式分別根據是Http還是Http/2請求,這裏就看一下Http1Codec的實現吧。
httpCodec.writeRequestHeaders(request);//1.開始寫入header
//寫入結束
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;//首先構建了一個null的responseBuilder。
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.
//2.當Header爲Expect: 100-continue時,只發送請求頭
// 當Header爲Expect: 100-continue時,只發送請求頭
// 發送一個請求, 包含一個Expect:100-continue, 詢問Server使用願意接受數據
// 接收到Server返回的100-continue應答以後, 才把數據POST給Server
// 1.如果可以繼續請求,則responseBuilder=null
// 2.如果不行,則responseBuilder不爲空,並且爲返回的Header
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();//刷新請求
realChain.eventListener().responseHeadersStart(realChain.call());
//讀取response的header信息,並返回一個responseBuilder賦值給responseBuilder。
responseBuilder = httpCodec.readResponseHeaders(true);
}
//如果可以繼續請求,則Responsebuilder=null,執行if判斷裏的內容,可以看到就是對於請求體的寫入操作,當然任然是使用Okio進行寫入操作。
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
//得到響應後,根據Resposne判斷是否寫入請求體
// 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);
//3.寫入請求體
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();
}
}
//4.結束請求
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
//5.得到響應頭
responseBuilder = httpCodec.readResponseHeaders(false);
}
//6.構建初步響應
// 通過返回得到的responseBuilder構建攜帶有響應頭的Reponse。
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.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
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
// 剩下的其實就是對弈返回碼的判斷,對應正常的返回碼的話,構建響應體到Response中,最後將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(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
//返回響應
return response;
}
總結
1.首先發現了計算機網絡的重要性,Http協議,準備後面入一本書,再回顧學習一下。
2.線程安全各種方式,各種數據結構。
3.設計模式,粗略回顧一下:建造者模式,責任鏈模式,策略模式…其實不用強行使用設計模式,其實主要是設計思想
4.面向接口編程,這個不用說,越看越重要。
5.方法的命名,其實我感覺挺有趣,也挺講究的。
6.註釋,其實和5差不多,基本上就是編程風格了。
附件
1)demo
https://github.com/ouyangxizhu/okhttp-demo.git
2)origin-3.12.x
https://github.com/ouyangxizhu/okhttp-origin-3.12.x.git