創建Client的外部調用
OKHttpClient client = new OkHttpClient.Builder().readTimeOut(5,TimeUnit.SECONDS).build();
先來看下OkHttpClient的內部類Builder的構造方法:
public Builder() {
dispatcher = new Dispatcher(); //okhttp請求的分發器,由它決定異步請求是直接處理,還是進行緩存等待。
//當然,他對同步請求並沒有做太多操作,只是把它的同步請求放到隊列中去執行。
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();//它其實是一個連接池,我們可以這樣理解,客戶端和服務端之間的連接,
//我們可以把它抽象爲一個connection,
//而每一個connection,我們都會把它放在connectionPool這個連接池當中,他來進行一個統一的管理,
//當你請求到的url是相同的時候,你就可以選擇複用,這是作用之一;
//另一個作用就是connectionPool這個類它實現了哪一些網絡連接它給保持打開狀態,哪一些是可以用來複用的,
//這些策略的設置,也是需要connectionPool這個連接池來進行管理的。
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
這裏這麼多參數在後面的一些請求流程中都會使用到,這就是Builder創建對象的模式,用Builder對象來封裝OkHttp初始化所需要的參數,然後傳遞我們的Builder對象到OkHttpClient的構造方法裏,完成它的整個對象屬性的初始化。
當你創建一個對象時,如果這個對象需要很多的參數,這時候,你可以使用到Builder這個創建模式,是一個很好的解決辦法。
創建Request請求報文信息類
Request的創建方式也是通過Builder來創建的,然後,通過鏈式調用,給Request指定url,或者頭部,以及方法等等。
外部調用如下:
Request request = new Request.Builder().url("http://www.baidu.com").get().build();
先來看下Request的內部類Builder的構造方法。
public Builder() {
this.method = "GET";//指定了請求方式爲GET,它是默認的
this.headers = new Headers.Builder();//創建了Headers的Builder對象來保存它的頭部信息
}
接下來看下build()方法,在build()方法裏面,他是直接創建一個Request對象,然後把當前的build對象傳遞過去,它的意思是把之前配置好的請求方式,url,頭部,都賦值給Request對象。
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
還有Request的構造方法。Request構造方法就是爲其指定了請求方式,以及請求的網絡地址url,以及它的頭部信息等等,這樣就完成了OkHttp同步請求的前兩步。
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.duplex = builder.duplex;
this.tags = Util.immutableMap(builder.tags);
}
創建Call對象
外部調用:
Call call = client.newCall(request);
它是通過client的newCall來進行實現的。
/**
* Prepares the {@code request} to be executed at some point in the future.
* Call類是一個接口
* Call類是一個準備執行的請求,可以被取消,代表着一個單一的請求/響應流,不能被執行兩次。
* New一個Call的子類RealCall去執行execute方法
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
由於Call是一個接口,所以它的實際操作都是在它實現類RealCall中所做的,下面來看一下RealCall中是怎麼做的。
下面來看下RealCall.java中是怎麼做的。
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;
}
首先,它先創建了它的實現類RealCall對象,然後,還賦值了一個Listener,就返回了。
它究竟做了哪些工作呢?我們到RealCall 的構造方法中看一下。
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);
}
RealCall其實是持有了前兩步初始化好的okHttpClient,originalRequest,同時,還賦值了一個重定向攔截器。
不管是同步,還是異步請求,它都是調用了client.newCall這個方法來進行創建的,然後我們通過創建好的Call對象進行操作。
使用execute完成同步請求
Response response = call.execute();
看看Call.java的execute()方法做了哪些操作。
/**
* Invokes the request immediately, and blocks until the response can be processed or is in
* error.
*
* <p>To avoid leaking resources callers should close the {@link Response} which in turn will
* close the underlying {@link ResponseBody}.
*
* <pre>{@code
*
* // ensure the response (and underlying response body) is closed
* try (Response response = client.newCall(request).execute()) {
* ...
* }
*
* }</pre>
*
* <p>The caller may read the response body with the response's {@link Response#body} method. To
* avoid leaking resources callers must {@linkplain ResponseBody close the response body} or the
* Response.
*
* <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
* not necessarily indicate application-layer success: {@code response} may still indicate an
* unhappy HTTP response code like 404 or 500.
*
* @throws IOException if the request could not be executed due to cancellation, a connectivity
* problem or timeout. Because networks can fail during an exchange, it is possible that the
* remote server accepted the request before the failure.
* @throws IllegalStateException when the call has already been executed.
*/
Response execute() throws IOException;
execute()是一個接口,再來看下它的具體實現,這是RealCall所做的具體實現。
/*
同步方法,不能在UI線程執行
*/
@Override
public Response execute() throws IOException {
synchronized (this) {
//是否執行過,如果執行過,拋出異常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
//調用Dispatcher的executed方法將該Call加入到一個雙端隊列裏
client.dispatcher().executed(this);
//返回本次的Response對象
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
//從已經執行的雙端隊列中移除本次Call
client.dispatcher().finished(this);
}
}
在這其中的同步代碼塊中,它首先判斷execute這個標誌是否爲true,它的意思是同一個http請求只能執行一次,沒有執行過,他就會在下面把execute置爲true,如果執行過,他就會拋出這個異常。
synchronized (this) {
//是否執行過,如果執行過,拋出異常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
接下來,他會捕捉一些Http請求的異常堆棧信息。
captureCallStackTrace();
然後,它會開啓了一個監聽事件,每當我們的call調用execute方法,或者enqueue方法,就會開啓這個Listener。
eventListener.callStart(this);
調用dispatcher()只是做了一個返回dispatcher對象的操作,dispatcher就是我們請求的分發器。
//調用Dispatcher的executed方法將該Call加入到一個雙端隊列裏
client.dispatcher().executed(this);
繼續進入這個executed方法。同步請求,它其實通過execute方法把它添加到隊列之中,完成了這個操作。
(在okhttp3-master/app/src/main/java/okhttp3/Dispatcher.java)
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
關於Dispatcher,它的作用就是維持Call請求發給它的狀態,同時,它也維護了一個線程池,用於執行網絡請求,而Call這個請求在執行任務的時候,通過Dispatcher這個分發器類,把它的任務推到執行隊列當中,然後來進行操作。
executed()方法主要是將我們的請求加入到同步請求隊列當中,這個同步請求隊列是在哪裏定義的呢?它其實是在Dispatcher.java中定義的。
//正在運行的同步任務,包含取消尚未完成的調用
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
繼續接下來execute()的同步方法,再獲取response,這個其實是攔截器鏈的方法,在這個類內部會一次調用攔截器進行操作。
//返回本次的Response對象
Response result = getResponseWithInterceptorChain();
往下走,在finally{}裏,它會主動地回收它的某些同步請求。
finally {
//從已經執行的雙端隊列中移除本次Call
client.dispatcher().finished(this);
}
進入finished方法看看,在Dispatcher.java裏面。
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call);
}
它又調用了另外一個Finish方法,把正在執行的同步請求傳入進來。
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
//移除本次call。
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
它首先做的操作就是把隊列中移除這個請求,如果不能移除,就拋出異常。
synchronized (this) {
//移除本次call。
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
表示Dispatcher分發器中沒有可運行的請求了,纔可調用run方法。
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
關於請求的判斷在這個方法裏。
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分發器做的事情非常簡單,一是保存同步請求;二是移除同步請求。