【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();//特別注意 這裡要在子執行緒執行
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
或者可以使用:
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());
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
execute執行流程原始碼分析
要看execute就要先看
Call call = mOkHttp.newCall(qqRequest);
- 1
- 1
點進去原始碼我們發現 返回了一個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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
現在我們找到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);//從請求佇列中移除
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
首先 我們看下一不太重點的兩行程式碼:
···
client.dispatcher().executed(this);//加入到請求佇列
···
client.dispatcher().finished(this);//從請求佇列中移除
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
這裡是用來幹嘛的?
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();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
從上面這段程式碼可以看出,它是用來將這個請求加入佇列中的,方便我們取消任務的。
2.ApplicationInterceptorChain與proceed()
搞懂了之後,我們來看看execute執行的那一行核心程式碼:
//核心程式碼!
Response result = getResponseWithInterceptorChain(false);
- 1
- 2
- 1
- 2
這裡呼叫了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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
相信看完上面的註釋,我相信大家也有一定的瞭解了。
有一個地方需要大家注意的,在官方文件中關於Application Interceptors有一句這樣的話:
Permitted to short-circuit and not call Chain.proceed().
意思是,我們可以在攔截器中,不呼叫proceed()。 但是proceed正是返回我們Response的方法啊!
我不太明白這個設計理念!
就此問題,我專門去OkHttp的Issues提問,有幸獲得了JakeWharton大神的回答(沒錯,那位JakeWharton)
附上鍊接:
What’s mean “Permitted to short-circuit and not call Chain.proceed().”?
請原諒我的渣英語。(逃
JakeWharton大神是這樣說的:
意思是說:這樣的設計是用在Http快取裡面的。執行請求之前,我們可以先從本地讀取,看看有沒有這個請求的快取。如果沒有,我們就呼叫proceed的方法!
但是需要再次注意,就算我們不呼叫這個proceed方法,也一定要返回一個Response物件! 這樣才可以避免丟擲空指標異常。:
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
分析完上面之後,我們發現,它呼叫了:
// No more interceptors. Do HTTP.
//當執行完攔截器之後,執行真正的Http請求。
return getResponse(request, forWebSocket);
- 1
- 2
- 3
- 1
- 2
- 3
這裡才是真正執行Http請求的地方。
關於getResponse具體內容,我就不再繼續分析了….因為需要的篇幅比較大,我會在接下來的文章進行分析! 先大概瞭解是用HttpEngine這個類來進行請求的!
結束
OK!關於execute方法,就分析地差不多了。應該也講地比較清楚了,請務必搞懂上面的內容,因為enqueue的方法是呼叫execute的方法的!
所以要是看不懂execute…那enqueue也看不懂了!
下篇文章將帶來enqueue的原始碼流程分析!(雖然你看懂了execute之後,也可以自己嘗試看看enqueue的原始碼了!)
備註
關於攔截器,這不屬於本文範圍,將在後續的文章中進行講解。
這裡也附上一些學習的連結:
官方Wiki Interceptor
或者可以看我另外一篇文章(這裡是專案中的實際應用,品完官方文件,食用更佳):
【專案重構】使用OkHttp解決Session過期導致使用者掉線的問題
-Hans 2016.4.3 21:30
轉自:http://blog.csdn.net/qq_18402085/article/details/51052167
相關文章
- 【OkHttp3原始碼分析】(二)Request的enqueueHTTP原始碼ENQ
- OkHttp3原始碼分析[DiskLruCache]HTTP原始碼
- OkHttp3原始碼分析[綜述]HTTP原始碼
- okhttp3 攔截器原始碼分析HTTP原始碼
- OkHttp3原始碼分析[快取策略]HTTP原始碼快取
- OkHttp3原始碼分析[任務佇列]HTTP原始碼佇列
- OkHttp3原始碼分析[複用連線池]HTTP原始碼
- OkHttp3原始碼解析(一)之請求流程HTTP原始碼
- ThinkPHP6 原始碼分析之解析 RequestPHP原始碼
- ApiView/Request類原始碼分析/序列化器APIView原始碼
- drf快速使用 CBV原始碼分析 drf之APIView分析 drf之Request物件分析原始碼APIView物件
- 生命週期 6--一個 request 是如何匹配到具體路由的原始碼分析路由原始碼
- 深度 Mybatis 3 原始碼分析(一)SqlSessionFactoryBuilder原始碼分析MyBatis原始碼SQLSessionUI
- OkHttp3原始碼解析(三)——連線池複用HTTP原始碼
- Android OkHttp3原始碼詳解——整體框架AndroidHTTP原始碼框架
- 原始碼|jdk原始碼之HashMap分析(一)原始碼JDKHashMap
- MyBatis原始碼分析(一)MyBatis原始碼
- preact原始碼分析(一)React原始碼
- Redux原始碼分析(一)Redux原始碼
- RecyclerView 原始碼分析(一)View原始碼
- AFL原始碼分析(一)原始碼
- 原始碼分析一:EventBus原始碼
- Backbone原始碼分析(一)原始碼
- Cobar 原始碼分析(一)原始碼
- YYCache 原始碼分析(一)原始碼
- Picasso原始碼分析(四):不變模式、建造者模式和Request的預處理原始碼模式
- Axios 原始碼解讀 —— request 篇iOS原始碼
- $request->validate()原始碼解讀原始碼
- LinkedList原始碼分析(一)原始碼
- DelayQueue系列(一):原始碼分析原始碼
- Retrofit原始碼分析三 原始碼分析原始碼
- 《YYModel原始碼分析(一)YYClassInfo》原始碼
- Netty原始碼分析--NIO(一)Netty原始碼
- DataTable.js原始碼分析(一)JS原始碼
- Volcano 原理、原始碼分析(一)原始碼
- Retrofit原始碼分析一 概覽原始碼
- AFNetworking 原始碼分析(一)原始碼
- Tinker接入及原始碼分析(一)原始碼