重要的類
類名 | 描述 |
---|---|
OkHttpClient | OkHttp請求用戶端,Builder模式實作 |
Dispatcher | 本質是異步請求的排程器,負責排程異步請求的執行,控制最大請求并發數和單個主機的最大并發數,并持有有一個線程池負責執行異步請求,對同步請求隻是作統計操作。 |
Request | 封裝網絡請求,就是建構請求參數(如url,header,請求方式,請求參數),Builder模式實作 |
Response | 網絡請求對應的響應,Builder模式實作,真正的Response是通過RealCall.getResponseWithInterceptorChain()方法擷取的。 |
Call | 是根據Request生成的一個具體的請求執行個體,且一個Call隻能被執行一次。 |
ConnectionPool | 連接配接池 |
Interceptor | Interceptor可以說是OkHttp的核心功能,它就是通過Interceptor來完成監控管理,重寫和重試請求的。 |
Cache | 可以自定義是否采用緩存,緩存形式是磁盤緩存,DiskLruCache。 |
源碼思想
OkHttp3,網絡請求庫,同步請求RealCall.execute()和異步請求RealCall.enqueue(),請求任務都是交給Dispatcher排程請求任務的處理,請求通過一條攔截鍊,每一個攔截器處理一部分工作,最後一個攔截器,完成擷取請求任務的響應,會将響應沿着攔截鍊向上傳遞。
源碼流程
初始化一個OkHttpClient對象
OkHttpClient mOkHttpClient = new OkHttpClient();
public OkHttpClient() {
this(new Builder());
}
Builder是OkHttpClient的一個内部類,目的是構造和封裝資料
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
proxySelector = ProxySelector.getDefault();
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;
connectTimeout = _000;
readTimeout = _000;
writeTimeout = _000;
}
建立一個請求
Request類是HTTP請求,它攜帶了請求位址、請求方法、請求頭部、請求體以及其他資訊。它也是通過Builder模式建立的。
Request request = new Request.Builder()
.url(url)
.build();
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
private Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
接下來對應的就是請求,請求分為異步與同步,先看同步
Response response = mOkHttpClient.newCall(request).execute();
Response是HTTP響應,它繼承自Closeable(Closeable繼承自AutoCloseable,AutoCloseable資源自動關閉的接口),它攜帶了請求、網絡請求協定、傳回狀态碼、請求頭、響應體等等,其中,網絡請求協定是Protocol,Protocol是一個枚舉類型,包含4中類型
public enum Protocol {
HTTP_1_0("http/1.0"),
HTTP_1_1("http/1.1"),
SPDY_3("spdy/3.1"),
HTTP_2("h2");
}
Call類是一個抽象類
OkHttpClient.newCall(request);
@Override
public Call newCall(Request request) {
return new RealCall(this, request); //RealCall extends Call
}
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client;
this.originalRequest = originalRequest;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
}
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);
}
}
OKHttp提供了execute()方法(同步方法)和enqueue()方法(異步方法),下面我們先看看execute()方法(同步方法)
execute()方法,首先判斷是否已執行過,如果已經執行過,則抛出異常資訊,也就是說一次Call執行個體隻能調用一次execute()方法。沒有執行則通過分發器進行執行
@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();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
分發器将請求加入到隊列
...
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//雙端隊列
...
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
最後通過分發器的finished()方法結束請求
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == && idleCallback != null) {
idleCallback.run();
}
}
是以通過上面資訊可知真正發送請求的地方是getResponseWithInterceptorChain()方法
真正發送請求源碼分析
組裝各種攔截器,然後發送請求
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
}
攔截器接口
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
在這個責任鍊模式中執行每一個連接配接器的proceed方法,然後我們逐個分析這些連接配接器
這裡是責任鍊模式,由使用者主動調用此方法,是以需要實作攔截的功能
這裡如果有Intercepter實作已經添加,則為每一個攔截器建立一個ApplicationInterceptorChain然後遞歸直到将所有的鍊調用完成最後進入getResponse方法中。
@Override public Response proceed(Request request) throws IOException {
//If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
//建立一個環節,并且将索引+1,這裡的request可能不再是originalRequest了,因為在攔截器中可能被修改了
Interceptor.Chain chain = new ApplicationInterceptorChain(index + , request, forWebSocket);
//擷取到對應的攔截器
Interceptor interceptor = client.interceptors().get(index);
//執行攔截器的intercept方法,參數是上面建立的環節,這個方法裡面會調用chain.proceed(),遞歸了。
//在最深的一層調用getResponse之後,響應會一層層的往外傳
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
// 傳回這一層攔截器處理用的響應
return interceptedResponse;
}
//遞歸到了最深的一層,攔截器都進入過了,發送httpRequest,擷取到response.
return getResponse(request, forWebSocket);
}
當執行完getResponse完成之後就一步步傳回。
真正請求資料的方法
- request.newBuilder();由于是之前的請求,是以内部調用newBuilder的時候使用老請求的url,method,body,tag,headers。然後給這個requestBuilder中添加各種請求的鍵值對資訊最後構造一個原資訊+新資訊的新請求。
- 建立一個請求引擎,每一個請求引擎代表依次請求/響應
- 在死循環中做的事情
- 如果請求被取消抛出異常
- 然後發送請求,讀取響應,重新連接配接設定成false
- 如果請求失敗則算了不請求了,但是如果是路由異常則需要恢複請求。
- 最後釋放連結,得到響應。
- 如果存在下一步請求,則重定向。
Response getResponse(Request request, boolean forWebSocket) throws IOException {
// 複制請求頭,并設定适合的屬性。如果長度不為-1,則設定Content-Length,否則使用chunked方式傳輸。
// 如果對chunked不熟悉,請參考其他資料
RequestBody body = request.body();
if (body != null) {
// 根據傳進來的request建立新的Builder.
Request.Builder requestBuilder = request.newBuilder();
// 設定Content-Type
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
// 判斷使用何種方式傳輸
long contentLength = body.contentLength();
// body長度不為-1,設定Content-Length
if (contentLength != -) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
// body 長度為 -1 ,使用chunked傳輸
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
//建立新請求
request = requestBuilder.build();
}
// 建立一個新的引擎,每個引擎代表一次請求/響應對
engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);
int followUpCount = ; //重試次數
//死循環 出口:
// 1. 請求被取消
// 2. 請求有問題
// 3. 捕獲到異常,且嘗試恢複失敗
// 4. 擷取到響應,且無需重定向
// 5. 重定向次數超過最大限制
while (true) {
// 被取消的情況
if (canceled) {
engine.releaseStreamAllocation();
throw new IOException("Canceled");
}
boolean releaseConnection = true;
try {
// 發送請求
engine.sendRequest();
// 讀取響應
engine.readResponse();
releaseConnection = false;
} catch (RequestException e) {
// 請求失敗,請求本身有問題,或者是網絡不通
throw e.getCause();
} catch (RouteException e) {
// 連接配接到伺服器的路由發生異常,請求還沒被發送
// 通過上一次連接配接異常恢複引擎
HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
// 如果恢複成功,将目前的引擎設定為這個恢複好的引擎
if (retryEngine != null) {
releaseConnection = false;
engine = retryEngine;
continue;
}
// 沒法恢複,抛出異常
throw e.getLastConnectException();
} catch (IOException e) {
// 與伺服器互動失敗,這時,請求可能已經被發送
// 恢複引擎
HttpEngine retryEngine = engine.recover(e, null);
//如果恢複成功,将目前的引擎設定為這個恢複好的引擎
if (retryEngine != null) {
releaseConnection = false;
engine = retryEngine;
continue;
}
// 沒法恢複,抛出異常
throw e;
} finally {
// 如果需要釋放連接配接,則将連接配接釋放
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;
}
// 使用重定向後的請求,重新執行個體一個引擎,開始下一次循環
request = followUp;
engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
response);
}
}
最後HttpEngine進行的操作與http協定很大相關,作者對此不感興趣,本文隻是對流程做大概分析
哥哥若是喜歡可移駕公衆号:碼老闆
