這篇文章主要梳理一下 OkHttp 的請求流程,對 OkHttp 的實現原理有個總體的把握,再深刻細節的實現會更加容易。html
建議將 OkHttp 的源碼下載下來,使用 IDEA 編輯器能夠直接打開閱讀。我這邊也將最新版的源碼下載下來,進行了註釋,有須要的能夠直接從 這裏 下載查看。java
咱們先看一下 OkHttp 的基本使用。android
// 一、建立 Request
Request request = new Request.Builder()
.get()
.url("xxx")
.build();
// 二、建立 OKHttpClient
OkHttpClient client = new OkHttpClient();
// 三、建立 Call
Call call = client.newCall(request);
try {
// 四、同步請求
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
// 五、異步請求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
複製代碼
上面的代碼中,首先構建一個請求 Request 和一個客戶端 OkHttpClient,而後 OkHttpClient 對象根據 request 調用 newCall
方法建立 Call 對象,再調用 execute
或者 enqueue
方法進行同步或者異步請求。git
接下來咱們看一看關鍵類和關鍵流程的具體實現。github
Request 類封裝了一次請求須要傳遞給服務端的參數:請求 method 如 GET/POST 等、一些 header、RequestBody 等等。web
Request 類未對外提供 public 的構造函數,因此構建一個 Request 實例須要使用構造者模式構建。緩存
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);
}
複製代碼
OkHttpClient 支持兩種構造方式。安全
一種是默認的構造方式:bash
OkHttpClient client = new OkHttpClient();
複製代碼
看一下構造函數:服務器
public OkHttpClient() {
this(new Builder());
}
複製代碼
這裏 OkHttpClient 內部默認配置了一些參數。
OkHttpClient(Builder builder) {...}
複製代碼
另外一種是經過 Builder 配置參數,最後經過 build
方法構建一個 OkHttpClient 對象。
OkHttpClient client = new OkHttpClient.Builder().build();
public OkHttpClient build() {
return new OkHttpClient(this); // 這裏的 this 是 Builder 實例
}
複製代碼
咱們看一下 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;
複製代碼
Call 是一個接口,是請求的抽象描述,具體實現類是 RealCall,經過Call.Factory 建立。
public interface Call extends Cloneable {
// 返回當前請求
Request request();
// 同步請求方法
Response execute() throws IOException;
// 異步請求方法
void enqueue(Callback responseCallback);
// 取消請求
void cancel();
// 請求是否在執行(當execute()或者enqueue(Callback responseCallback)執行後該方法返回true)
boolean isExecuted();
// 請求是否被取消
boolean isCanceled();
Timeout timeout();
// 建立一個新的如出一轍的請求
Call clone();
interface Factory {
Call newCall(Request request);
}
}
複製代碼
OkHttpClient 實現了 Call.Factory,負責根據 Request 建立新的 Call:
Call call = client.newCall(request);
複製代碼
看一下 newCall
方法。
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
複製代碼
這裏咱們發現實際上調用了 RealCall 的靜態方法 newRealCall
, 不難猜想 這個方法就是建立 Call 對象。
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 的 execute
方法。
@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();
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);
}
}
複製代碼
這裏主要作了這幾件事:
getResponseWithInterceptorChain()
函數獲取 HTTP 返回結果。dispatcher
本身已經執行完畢,將 call 從 runningSyncCalls 隊列中移除。這裏涉及到了 Dispatcher 這個類,咱們在異步請求這一節中再介紹。
真正發出網絡請求以及解析返回結果的是在 getResponseWithInterceptorChain
方法中進行的。
Response getResponseWithInterceptorChain() throws IOException {
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);
複製代碼
getResponseWithInterceptorChain
方法的代碼量並很少,可是卻完成了全部的請求處理過程。
這裏先是建立了一個 Interceptor 的集合,而後將各種 interceptor 所有加入到集合中,包含如下 interceptor:
添加完攔截器後,建立了一個 RealInterceptorChain 對象,將集合 interceptors 和 index(數值0)傳入。接着調用其 proceed
方法進行請求的處理,咱們來看 proceed
方法。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
...
// 建立下一個RealInterceptorChain,將index+1(下一個攔截器索引)傳入
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
// 獲取當前的攔截器
Interceptor interceptor = interceptors.get(index);
// 經過Interceptor的intercept方法進行處理
Response response = interceptor.intercept(next);
...
return response;
}
複製代碼
咱們來看一些關鍵代碼:
RealInterceptorChain 的 proceed
方法先建立 RealInterceptorChain 的對象,將集合 interceptors 和 index + 1 傳入。從前面的分析知道,初始 index 爲 0。
而後獲取當前 index 位置上的 Interceptor,將建立的 RealInterceptorChain 對象 next 傳入到當前攔截器的 intercept
方法中,intercept
方法內部會調用 next 的 proceed 方法,一直遞歸下去,最終完成一次網絡請求。
因此每一個 Interceptor 主要作兩件事情:
異步請求調用的是 RealCall 的 enqueue
方法。
public void enqueue(Callback responseCallback) {
synchronized(this) {
if (this.executed) {
throw new IllegalStateException("Already Executed");
}
this.executed = true;
}
this.captureCallStackTrace();
this.eventListener.callStart(this);
this.client.dispatcher().enqueue(new RealCall.AsyncCall(responseCallback));
}
複製代碼
與同步請求同樣,異步請求也涉及了一個重要的參與者 Dispatcher,它的做用是:控制每個 Call 的執行順序和生命週期。它內部維護了三個隊列:
對於同步請求,因爲它是即時運行的, Dispatcher 只須要運行前請求前存儲到 runningSyncCalls,請求結束後從 runningSyncCalls 中移除便可。
對於異步請求,Dispatcher 是經過啓動 ExcuteService 執行,線程池的最大併發量 64,異步請求先放置在 readyAsyncCalls,能夠執行時放到 runningAsyncCalls 中,執行結束從runningAsyncCalls 中移除。
咱們看一下具體實現細節,下面是 Dispatcher 的 enqueue
方法,先將 AsyncCall 添加到 readyAsyncCalls。
void enqueue(AsyncCall call) {
// 將AsyncCall加入到準備異步調用的隊列中
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
複製代碼
再看 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;
}
複製代碼
這裏主要的工做有:
executableCalls
列表中。executableCalls
取出請求 AsyncCall 對象,調用其 executeOn
方法。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!
}
}
}
複製代碼
能夠看到 executeOn
方法的參數傳遞的是 ExecutorService 線程池對象,方法中調用了線程池的 execute
方法,因此 AsyncCall 應該是實現了 Runnable 接口,咱們看看它的 run
方法是怎樣的。
AsyncCall 繼承自 NamedRunnable 抽象類。
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
方法會走到 NamedRunnable 的 run
方法,run
方法中又調用了 抽象方法 execute
,咱們直接看 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);
}
}
}
複製代碼
這裏咱們又看到了熟悉的 getResponseWithInterceptorChain
方法。
這樣看來,同步請求和異步請求的原理是同樣的,都是在 getResponseWithInterceptorChain()
函數中經過 Interceptor 鏈條來實現的網絡請求邏輯。
以上即是 Okhttp 整個請求與響應的具體流程,OkHttp 流程圖以下。
簡述 OkHttp 的請求流程:
getResponseWithInterceptorChain
方法實現。getResponseWithInterceptorChain
方法中採用了責任鏈模式,每個攔截器各司其職,主要作兩件事。