OkHttpClient同步請求的執(zhí)行流程和源碼分析
同步請求示例
OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("https://www.baidu.com").get().build();
Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
Log.e(TAG,"response: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
同步請求的步驟
- 創(chuàng)建OkHttpClient對象和Request對象,均是采用Builder模式創(chuàng)建,構(gòu)建者(Builder)設(shè)計模式(又叫生成器設(shè)計模式)
- 將Request封裝成Call對象
- 調(diào)用Call的execute()方法發(fā)送同步請求,發(fā)送請求后,就會進入阻塞狀態(tài),直到收到響應(yīng)。
一、(1)OkHttpClient Builder對象分析
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
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 = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
??OkHttpClient Builder的構(gòu)造函數(shù),主要是對一些參數(shù)賦值默認值,對一些對象進行初始化,Dispatcher是OkHttpClient中http請求的分發(fā)器,由它來決定異步請求是直接處理還是進行緩存等待,對于同步請求,它并沒有做太多操作,只是把同步請求放到隊列當(dāng)中去執(zhí)行。ConnectionPool是一個連接池對象,用于管理連接對象,當(dāng)存在同樣的Url請求時,可以復(fù)用,從連接池中找到對應(yīng)緩存的連接對象。
(2)Request 對象分析
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
Request Builder的構(gòu)造函數(shù),默認請求方法為GET,同時初始化一個Header對象。
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
build()方法是創(chuàng)建Request對象,將當(dāng)前的builder對象傳入,接下來看Request的構(gòu)造函數(shù):
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);
}
??可以看到,是將傳入的Builder對象中的屬性賦值給Request的相關(guān)屬性,這樣就創(chuàng)建好了Request對象。
二、創(chuàng)建Call 對象分析
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
??OkHttpClient 對象中的newCall()方法,返回值是一個Call對象(接口),在這里可以看到實際上調(diào)用的RealCall.newRealCall()方法創(chuàng)建,RealCall是Call接口的一個實現(xiàn)類,接著查看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;
}
??可以看到newRealCall()方法中創(chuàng)建了Call接口的實現(xiàn)類RealCall對象并返回該對象,到此Call對象的創(chuàng)建便完成了。
三、Call 對象exexcute()方法分析
??上面有提及到Call對象是一個接口,我們點擊查看exexcute()方法時,需要點擊查看該方法的實現(xiàn),實際上是進入到RealCall對象的exexcute()方法:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
??在同步代碼中,先通過判斷executed標(biāo)識,如果當(dāng)前已經(jīng)有在執(zhí)行,則會拋出"Already Executed"信息的異常,如果沒有執(zhí)行過,則更改executed標(biāo)識為true。
??接著調(diào)用captureCallStackTrace()方法,這個方法主要用于捕捉一些http請求的異常堆棧信息。
??eventListener.callStart(this)開啟事件監(jiān)聽,通過查看該方法:
/**
* Invoked as soon as a call is enqueued or executed by a client. In case of thread or stream
* limits, this call may be executed well before processing the request is able to begin.
*
* <p>This will be invoked only once for a single {@link Call}. Retries of different routes
* or redirects will be handled within the boundaries of a single callStart and {@link
* #callEnd}/{@link #callFailed} pair.
*/
public void callStart(Call call) {
}
??通過閱讀該方法的注釋,可以知道該方法會在調(diào)用Call對象的enqueue()或execute()方法的時候,就會開啟這個listener。
接下來分析一下這個方法中的核心代碼:
client.dispatcher().executed(this);
首先調(diào)用OkHttpClient的dispatcher()方法
public Dispatcher dispatcher() {
return dispatcher;
}
該方法返回一個Dispatcher對象,緊接著調(diào)用該對象的executed()方法:
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
??該方法中,runningSyncCalls是一個存放同步請求的隊列,這里僅僅只是將RealCall加入到同步請求的隊列中,Dispatcher對象中相關(guān)的隊列有:
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
- readyAsyncCalls 是異步請求的就緒隊列
- runningAsyncCalls 是異步請求的執(zhí)行隊列
- runningSyncCalls 是同步請求的執(zhí)行隊列
??調(diào)用完Dispatcher的executed()方法后,緊接著調(diào)用getResponseWithInterceptorChain()方法獲取Response對象,這個其實是一個攔截器鏈的方法,該方法內(nèi)部會依次調(diào)用攔截器來進行相應(yīng)的操作。
最后看一下finally中:
finally {
client.dispatcher().finished(this);
}
??通過調(diào)用Dispatcher的finished()方法,傳入當(dāng)前的RealCall對象,查看該方法的代碼可以發(fā)現(xiàn):
/** Used by {@code Call#execute} to signal completion. */
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 == 0 && idleCallback != null) {
idleCallback.run();
}
}
??該方法繼續(xù)調(diào)用了其他一個同名的的方法,將正在執(zhí)行的同步請求隊列傳了進來,在同步代碼塊中,移除掉同步請求隊列中的call對象,并進行了判斷,如果移除出錯,則會拋出異常。接著判斷promoteCalls,由于這里傳入的promoteCalls為false,所以不會走promoteCalls()方法。
??接著,對runningCallsCount重新賦值,runningCallsCount用于記錄當(dāng)前正在執(zhí)行的請求數(shù),查看該方法的代碼:
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
該方法很簡單,即返回正在執(zhí)行的異步請求數(shù)和正在執(zhí)行的同步請求數(shù)的總和。
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
??最后通過判斷當(dāng)前正在執(zhí)行的請求數(shù),如果當(dāng)前沒有正在執(zhí)行的請求數(shù)并且有設(shè)置閑置時的回調(diào),則會回調(diào)其run()方法。
總結(jié)
??到此,同步請求的執(zhí)行流程就已經(jīng)分析完了,由上述的分析可以知道,在同步請求中,Dispatcher分發(fā)器做的工作非常簡單,就兩個操作,保存同步請求和移除同步請求
OkHttpClient異步請求的執(zhí)行流程和源碼分析
異步請求示例
OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("https://www.baidu.com").get().build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
異步請求的步驟
- 創(chuàng)建OkHttpClient對象和Request對象,均是采用Builder模式創(chuàng)建,構(gòu)建者(Builder)設(shè)計模式(又叫生成器設(shè)計模式)
- 將Request封裝成Call對象
- 調(diào)用Call的enqueue()方法進行異步請求
同步和異步的區(qū)別
- 發(fā)起請求的方法調(diào)用
- 阻塞線程與否
源碼分析
??異步請求的前兩步,和同步請求的一致,都是一些準(zhǔn)備工作,并沒有發(fā)起請求,這里不再重復(fù)說明,最主要的是第三步,調(diào)用Call對象的enqueue()方法,具體的實現(xiàn)還是在RealCall類中,查看該方法代碼:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
??前面的操作和同步請求的execute()方法相似,主要是 client.dispatcher().enqueue(new AsyncCall(responseCallback)) 這行代碼,調(diào)用Dispatcher的enqueue()方法,將Callback回調(diào)封裝成AsyncCall對象作為參數(shù)傳入,通過查看代碼,了解到AsyncCall對象繼承自NamedRunnable對象,而NamedRunnable對象實現(xiàn)了Runnable接口,接著繼續(xù)查看Dispatcher的enqueue()方法源碼:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
??該方法前加了synchronized修飾符,是一個同步方法,根據(jù)判斷當(dāng)前執(zhí)行的異步請求數(shù)是否小于maxRequests(最大請求數(shù),默認為64) 且當(dāng)前執(zhí)行的異步請求隊列中相同主機的請求數(shù)小于maxRequestsPerHost(每個主機最大請求數(shù),默認為5) 來進行處理,如果二者都小于設(shè)置的值,則將該請求添加到runningAsyncCalls(異步請求執(zhí)行隊列)中,否則則添加到readyAsyncCalls(異步請求準(zhǔn)備隊列)中。
runningCallsForHost()方法的代碼:
/** Returns the number of running calls that share a host with {@code call}. */
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.get().forWebSocket) continue;
if (c.host().equals(call.host())) result++;
}
return result;
}
??通過注釋可以知道,該方法返回同一個主機的請求數(shù)目,通過遍歷執(zhí)行中的異步請求隊列,和傳入的AsyncCall對象的主機對比,如果相同則記錄數(shù)遞增,以此獲得和傳入AsyncCall對象相同主機的請求數(shù)。
enqueue()方法中,主要的代碼:
executorService().execute(call);
這里是進行異步請求操作的代碼,先看下executorService()方法的代碼:
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;
}
??該方法也是一個同步方法,主要用于返回 ExecutorService 對象,在這里僅一次創(chuàng)建了線程池對象 ThreadPoolExecutor,第二個參數(shù)傳入了Integer的最大值,即線程池所能容納的最大線程數(shù)為Integer.MAX_VALUE,雖然這里設(shè)置了很大的值,但是實際情況下并非會達到最大值,因為上面enqueue()方法中有做了判斷,主要的還是maxRequests這個值決定異步請求線程池的最大數(shù)量。
??executorService()方法返回了線程池對象,接著調(diào)用它的execute()方法,傳入實現(xiàn)Runnable接口的AsyncCall對象,上面提及到AsyncCall繼承NamedRunnable,而NamedRunnable對象實現(xiàn)了Runnable接口,所以我們想知道該線程池執(zhí)行這個任務(wù)做了什么,就得看下NamedRunnable對象的 run() 方法:
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
該方法中,真正的處理邏輯是在execute()方法中:
protected abstract void execute();
而execute()方法是一個抽象方法,所以要回到繼承NamedRunnable對象的AsyncCall類中:
@Override protected void execute() {
boolean signalledCallback = false;
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) {
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);
}
}
??這里才是真正進行異步請求操作的邏輯,同樣也是通過getResponseWithInterceptorChain()方法得到Response對象,關(guān)于getResponseWithInterceptorChain()方法的分析在下面的文章里將會介紹,接著通過判斷retryAndFollowUpInterceptor是否取消回調(diào)CallBack接口的onFailure()或onResponse()方法,最后finally中,和同步請求的處理一樣,調(diào)用了Dispatcher對象的finished()方法:
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
也是調(diào)用了帶三個參數(shù)的finished()方法,傳入了runningAsyncCalls,call,第三個參數(shù)傳入了true。
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 == 0 && idleCallback != null) {
idleCallback.run();
}
}
這里的處理和同步請求結(jié)束后的處理多了一個promoteCalls()方法的調(diào)用,因為這里promoteCalls傳入了true,所以會走promoteCalls()方法:
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
??看完這個方法,會有一種恍然大悟的感覺,因為上面調(diào)用enqueue()方法的時候,會根據(jù)情況將請求添加到runningAsyncCalls(異步請求執(zhí)行隊列)或readyAsyncCalls(異步請求準(zhǔn)備隊列)中,而readyAsyncCalls隊列中的請求什么時候執(zhí)行呢,相信在看enqueue()方法的時候會有這個疑問,看了promoteCalls()后疑問將會被解答,為了方便閱讀再次貼上enqueue()方法:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
??promoteCalls()方法中,首先做了一些判斷,當(dāng)runningAsyncCalls(異步請求執(zhí)行隊列)已經(jīng)達到設(shè)置的最大的請求數(shù)或當(dāng)前readyAsyncCalls(異步請求準(zhǔn)備隊列)中沒有請求的時候,則直接返回不做處理,如果滿足條件,則會遍歷readyAsyncCalls隊列,將該請求添加到runningAsyncCalls隊列中,并調(diào)用 executorService().execute(call) 對該請求進行處理。
總結(jié)
??如果異步請求數(shù)超過最大請求數(shù)或同個主機最大請求數(shù)超過設(shè)置的值的時候,該請求就會添加到readyAsyncCalls(異步請求準(zhǔn)備隊列)中,當(dāng)執(zhí)行完runningAsyncCalls(異步請求執(zhí)行隊列)的請求后,將會調(diào)用Dispatcher的finished()三個參數(shù)的方法,第三個參數(shù)傳入true,會調(diào)用promoteCalls()方法,遍歷準(zhǔn)備隊列readyAsyncCalls,將該隊列的中的請求添加到執(zhí)行隊列runningAsyncCalls中,調(diào)用 executorService().execute(call)進行處理。
Dispatcher的作用
維護請求的狀態(tài),并維護一個線程池,用于執(zhí)行請求。
異步請求為什么需要兩個隊列
異步請求的設(shè)計可以將其理解成生產(chǎn)者消費者模式,其中各個角色分別為:
- Dispatcher 生產(chǎn)者
- ExecutorService 消費者池
- Deque<AsyncCall> readyAsyncCalls 緩存
- Deque<AsyncCall> runningAsyncCalls 正在運行的任務(wù)
當(dāng)同步和異步請求結(jié)束后,會調(diào)用dispatcher的finished方法,將當(dāng)前的請求從隊列中移除。
下一篇文章中,將為大家講解一下OkHttp的攔截器鏈,感興趣的朋友可以繼續(xù)閱讀:
OkHttpClient源碼分析(二) —— RetryAndFollowUpInterceptor和BridgeInterceptor