這篇文章簡要介紹OkHttp的請求和響應過程。
文章基于OkHttp3.14.3版本
0 前言
OkHttp作為當下Java系程式設計的網絡請求庫,其熱門程度自不必說了。網上有關OkHttp的使用、封裝和源碼分析的文章和代碼也早已是百家齊放、甚至可以說是爛大街了。然而盡管如此,筆者還是希望能夠将對OkHttp的學習和研究記錄下來形成自己的内容,友善以後檢視,于是開始寫吧,好記性不如爛筆頭。
那就從最簡單的開始。這篇文章打算簡要描述一下OkHttp中大緻的請求響應過程。
1 開始
首先看看簡單的同步GET請求和異步GET請求:
- 同步GET請求:
public static void getSync() {
// Step 1. 建立一個HttpClient執行個體用于建立請求任務
OkHttpClient httpClient = new OkHttpClient();
// Step 2. 建構一個Request用于封裝請求位址、請求類型、參數等資訊
Request request = new Request.Builder().get()
.url("https://www.baidu.com")
.build();
// Step 3. 建立一個新的請求任務Call
Call call = httpClient.newCall(request);
try {
// Step 4. 發起請求
Response response = call.execute();
// Step 5. 讀取、處理請求結果
ResponseBody responseBody = response.body();
if (responseBody != null) {
System.out.println(responseBody.string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
- 異步GET請求:
public void getAsync() {
// Step 1. 建立一個HttpClient執行個體用于建立請求任務
OkHttpClient httpClient = new OkHttpClient();
// Step 2. 建構一個Request用于封裝請求位址、請求類型、參數等資訊
Request request = new Request.Builder().get()
.url("https://www.baidu.com")
.build();
// Step 3. 建立一個新的請求任務Call
Call call = httpClient.newCall(request);
// Step 4. 發起請求
call.enqueue(new Callback() {
@Override
public void onFailure(final Call call, final IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(final Call call, final Response response) throws IOException {
// Step 5. 讀取、處理請求結果
ResponseBody responseBody = response.body();
if (responseBody != null) {
System.out.println(responseBody.string());
}
}
});
}
可以看到,不管是同步還是異步請求,都需要經過Step1~Step3三個步驟建構一個請求任務,并通過調用call.execute()/call.enqueue(callback)來執行同步/異步請求。那接下來就看看這兩個方法的執行過程吧。
首先看看call.execute():
點選檢視Call類,發現Call是一個接口,嘗試跳轉到call.execute()方法的具體實作(ps: AS快捷鍵Ctrl+Alt+B實作快速跳轉到方法的具體實作),來到Call接口的唯一實作類RealCall類,RealCall.execute()具體實作如下:
@Override public Response execute() throws IOException {
// Step 1.
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// Step 2.
transmitter.timeoutEnter();
transmitter.callStart();
try {
// Step 3.
client.dispatcher().executed(this);
// Step 4.
return getResponseWithInterceptorChain();
} finally {
// Step 5.
client.dispatcher().finished(this);
}
}
- Step 1.執行請求之前,確定該call請求任務尚未被執行過。由此可以看到OkHttp要求每個請求任務隻能被執行一次。
- Step 2.開始計算逾時時間,并記錄請求開始callStart這個流程。
- Step 3.通過Dispatcher将本次請求任務記錄到同步請求隊列runningSyncCalls。runningSyncCalls是一個實作為ArrayDeque的雙向隊列:
/** 正在運作的同步請求任務,包括尚未結束就已經取消同步請求. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
- Step 4. 接着調用RealCall.getResponseWithInterceptorChain()發起請求并擷取請求結果;
- Step 5.在傳回結果給上層調用之前,通過Dispatcher将本次任務從runningSyncCalls隊列中移除以表示本次請求任務結束:
- Step 6.傳回請求結果給上層,請求結束。
經過以上這幾個步驟,一次同步GET請求就算是結束了。可以發現,RealCall.getResponseWithInterceptorChain()方法負責進行具體的HTTP請求,這裡暫時不跟進去,先來看看異步的GET請求:
Ctrl+Alt+B
快速跳轉到**call.enqueue(callback)**的具體實作RealCall.enqueue(callback):
@Override public void enqueue(Callback responseCallback) {
// Step 1.
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// Step 2.
transmitter.callStart();
// Step 3.
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
- Step 1.與同步GET請求的Step 1相同。
- Step 2.記錄該請求過程。
- Step 3.構造一個AsyncCall對象并加入請求隊列。AsyncCall是RealCall的内部類,用于表示異步的請求任務。AsyncCall的父類NamedRunnable是一個實作了Runnable接口的抽象類并重寫了Runnable.run方法,同時對外提供了**execute()**抽象方法,并在run方法實作中調用。是以,接下來關注AsyncCall類實作的execute()方法,既然AsyncCall是一個Runnable,而run中又調用了execute(),可以判定,異步請求任務最終會執行到這裡并進行實際的HTTP請求:
@Override protected void execute() {
boolean signalledCallback = false;
// Step 3.1 開始計算請求逾時時間
transmitter.timeoutEnter();
try {
// Step 3.2 發起請求并擷取請求結果
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
// Step 3.3 請求成功,将結果通過回調接口傳回給上層
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 {
// 請求失敗
responseCallback.onFailure(RealCall.this, e);
}
} finally {
// Step 4.結束本次請求任務,并從隊列中移除
client.dispatcher().finished(this);
}
}
}
而查閱execute的實作也驗證了這個判定是正确的,至于何時,從哪兒,會執行到這個execute方法,先留個小坑吧後續再填~。同時,對比RealCall.execute()方法(同步請求)和AsyncCall.execute()方法(異步請求)發現,不管是同步還是異步請求,最後都會通過調用getResponseWithInterceptorChain()方法來實作網絡請求求并擷取傳回結果,看看這個方法的實作:
Response getResponseWithInterceptorChain() throws IOException {
// 建構一個完整的攔截器清單
List<Interceptor> interceptors = new ArrayList<>();
// 添加使用者自定義的攔截器
interceptors.addAll(client.interceptors());
// 添加用于失敗重試和重定向的攔截器
interceptors.add(new RetryAndFollowUpInterceptor(client));
// 添加用于連接配接應用層和網絡層的攔截器,該攔截器會将一個使用者請求轉換為網絡請求,并将網絡請求結果以使用者友好的方式傳回
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 添加用于緩存的攔截器
interceptors.add(new CacheInterceptor(client.internalCache()));
// 添加用于打開與伺服器間網絡連接配接的攔截器
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
// 如果本次請求任務不是websocket請求,則添加網絡攔截器,該攔截器也需要使用者自定義
interceptors.addAll(client.networkInterceptors());
}
// 添加最後一個攔截器,該攔截器負責執行最終的網絡請求并傳回結果
interceptors.add(new CallServerInterceptor(forWebSocket));
// 建構一個攔截器鍊chain對象
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
// 執行請求并擷取傳回結果
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
可以看到,這個方法最終是通過chain.proceed(originalRequest);實作請求和傳回結果,到這裡,OkHttp的請求過程就結束了。是以,一個OkHttp的請求過程大緻如下:
先到這裡吧。本來想着一篇寫完的但太長了似乎自己都不想看,還是一步步來吧。下一篇開始着重分析以上OkHttp請求過程中接觸到的各個關鍵的類。
2 The End ?
歡迎關注公衆号: 文章首發在個人部落格 https://www.nullobject.cn,公衆号NullObject同步更新。