【OkHttp3原始碼分析】(一)Request的execute
簡單使用OkHttp3
閱讀本文需要對OkHttp3的使用有一定了解。
首先我們先看看如何簡單進行一個get請求的Request。
Request qqRequest = new Request.Builder()
.url("http://www.qq.com")
.build();
Call call = mOkHttp.newCall(qqRequest);
call.execute();//特別注意 這裡要在子執行緒執行
或者可以使用:
Request qqRequest = new Request.Builder ()
.url("http://www.qq.com")
.build();
Call call = mOkHttp.newCall(qqRequest);
//使用enquue
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG, response.body().string());
}
});
execute執行流程原始碼分析
要看execute就要先看
Call call = mOkHttp.newCall(qqRequest);
點進去原始碼我們發現 返回了一個RealCall物件,因為Call是一個介面,而RealCall才是真正的實現類
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override
public Call newCall(Request request) {
return new RealCall(this, request);
}
現在我們找到execute方法的原始碼:
@Override
public Response execute() throws IOException {
synchronized (this) {//同步鎖,為了防止子執行緒同時呼叫而導致出現多次網路請求
if (executed) throw new IllegalStateException("Already Executed");
executed = true;//這裡表明,對應的Call只能執行一次
}
try {
client.dispatcher().executed(this);//加入到請求佇列
Response result = getResponseWithInterceptorChain(false);//核心程式碼!
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);//從請求佇列中移除
}
}
首先 我們看下一不太重點的兩行程式碼:
···
client.dispatcher().executed(this);//加入到請求佇列
···
client.dispatcher().finished(this);//從請求佇列中移除
這裡是用來幹嘛的?
1.Dispatcher輔助管理請求
我把相關程式碼貼出來:
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
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);
}
/** Used by {@code Call#execute} to signal completion. */
synchronized void finished(Call call) {
if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
}
/**
* Cancel all calls currently enqueued or executing. Includes calls executed both {@linkplain
* Call#execute() synchronously} and {@linkplain Call#enqueue asynchronously}.
*/
public synchronized void cancelAll() {
for (AsyncCall call : readyAsyncCalls) {
call.cancel();
}
for (AsyncCall call : runningAsyncCalls) {
call.cancel();
}
for (RealCall call : runningSyncCalls) {
call.cancel();
}
}
從上面這段程式碼可以看出,它是用來將這個請求加入佇列中的,方便我們取消任務的。
2.ApplicationInterceptorChain與proceed()
搞懂了之後,我們來看看execute執行的那一行核心程式碼:
//核心程式碼!
Response result = getResponseWithInterceptorChain(false);
這裡呼叫了getResponseWithInterceptorChain方法(廢話,我夠知道)- -#。 我們來看看相關程式碼:
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
}
class ApplicationInterceptorChain implements Interceptor.Chain {
private final int index;
private final Request request;
private final boolean forWebSocket;
ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {
this.index = index;
this.request = request;
this.forWebSocket = forWebSocket;
}
@Override public Connection connection() {
return null;
}
@Override public Request request() {
return request;
}
@Override public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
}
getResponseWithInterceptorChain裡面建立了一個ApplicationInterceptorChain物件,並且呼叫了它的proceed方法。
我們來看一下傳入的這個引數:0。 實際上這個0是有特殊含義的。
這裡面涉及了OkHttp3的新特性:攔截器Interceptor。 它可以新增多個攔截器,而這裡的0的意思,就是從第1個攔截器開始。
簡單瞭解完之後,我們看看關鍵的proceed程式碼
@Override
public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
// 判斷還有沒有攔截器? 有的話先走攔截器
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);//建立下一個攔截器的chain
Interceptor interceptor = client.interceptors().get(index);//獲取攔截器
Response interceptedResponse = interceptor.intercept(chain);//傳遞過去
//這裡要注意,攔截器返回的Response不能為空
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
//返回結果
return interceptedResponse;
}
// No more interceptors. Do HTTP.
//當執行完攔截器之後,執行真正的Http請求。
return getResponse(request, forWebSocket);
}
相信看完上面的註釋,我相信大家也有一定的瞭解了。
有一個地方需要大家注意的,在官方文件中關於Application Interceptors有一句這樣的話:
Permitted to short-circuit and not call Chain.proceed().
意思是,我們可以在攔截器中,不呼叫proceed()。 但是proceed正是返回我們Response的方法啊!
我不太明白這個設計理念!
請原諒我的渣英語。(逃
JakeWharton大神是這樣說的:
意思是說:這樣的設計是用在Http快取裡面的。執行請求之前,我們可以先從本地讀取,看看有沒有這個請求的快取。如果沒有,我們就呼叫proceed的方法!
但是需要再次注意,就算我們不呼叫這個proceed方法,也一定要返回一個Response物件! 這樣才可以避免丟擲空指標異常。:
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
分析完上面之後,我們發現,它呼叫了:
// No more interceptors. Do HTTP.
//當執行完攔截器之後,執行真正的Http請求。
return getResponse(request, forWebSocket);
這裡才是真正執行Http請求的地方。
關於getResponse具體內容,我就不再繼續分析了….因為需要的篇幅比較大,我會在接下來的文章進行分析! 先大概瞭解是用HttpEngine這個類來進行請求的!
結束
OK!關於execute方法,就分析地差不多了。應該也講地比較清楚了,請務必搞懂上面的內容,因為enqueue的方法是呼叫execute的方法的!
所以要是看不懂execute…那enqueue也看不懂了!
下篇文章將帶來enqueue的原始碼流程分析!(雖然你看懂了execute之後,也可以自己嘗試看看enqueue的原始碼了!)
備註
關於攔截器,這不屬於本文範圍,將在後續的文章中進行講解。
這裡也附上一些學習的連結:
官方Wiki Interceptor
-Hans 2016.4.3 21:30