【OkHttp3原始碼分析】(二)Request的enqueue
前言
如果沒有閱讀本系列文章的第一篇,請先閱讀:
【OkHttp3原始碼分析】(一)Request的execute
因為這兩者之間是有關聯的!
enqueue執行流程原始碼分析
先來看看原始碼:
@Override
public void enqueue(Callback responseCallback) {
enqueue(responseCallback, false);//繼續呼叫下面一層
}
void enqueue(Callback responseCallback, boolean forWebSocket) {
synchronized (this) {//跟execute一樣,防止多個子執行緒同時呼叫執行的方法。
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));//重點!
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
看到重點的那一行:
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));//重點!
- 1
- 1
我們發現,它將我們的Callback介面封裝到一個新的類AsyncCall中去。
我們來看看它的程式碼:
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private final boolean forWebSocket;
private AsyncCall(Callback responseCallback, boolean forWebSocket) {
super("OkHttp %s", originalRequest.url().toString());
this.responseCallback = responseCallback;
this.forWebSocket = forWebSocket;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
Object tag() {
return originalRequest.tag();
}
void cancel() {
RealCall.this.cancel();
}
RealCall get() {
return RealCall.this;
}
@Override //重點方法!
protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain(forWebSocket);//和execute呼叫的是同一個!
if (canceled) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));//回掉介面的失敗方法
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);//回掉介面的成功方法
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);//移除請求
}
}
}
- 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
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 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
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
要注意它是繼承NamedRunnable的,這裡我們從名字也可以看出,實際上它就是一個Runnable了。
簡單看看NamedRunnable原始碼:
/**
* Runnable implementation which always sets its thread name.
*/
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = String.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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
瞭解完之後,我們就可以知道哪個是重要的方法了。
沒錯就是:execute方法。(注意這個execute不是我們第一篇文章講的那個)
然後我們發現它呼叫了:
Response response = getResponseWithInterceptorChain(forWebSocket);//和execute呼叫的是同一個!
- 1
- 1
實際上這個和Request的execute呼叫是一樣的,只不過多了一個介面回掉的過程。這裡我們就不再重複分析了。
OK,瞭解完AnsycCall類,我們回來來看看這句的enqueue:
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));//重點!
- 1
- 1
首先我們都知道,OkHttp3中enqueue是非同步通過執行,然後通過介面回撥的!
再加上AsyncCall本質是一個Runnable。
所以用我們程式設計師本能的嗅覺,其實已經能夠大概擦覺到,這個client.dispatcher()通過自己封裝的執行緒池,執行我們的AsyncCall的execute方法了!
但是要是沒有察覺到也沒有關係,我們看看原始碼就知道是什麼了!
我們來看看Dispatcher類的enqueue方法:
synchronized void enqueue(AsyncCall call) {
//如果佇列任務沒有爆滿且小於每個Request的請求次數
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);//新增到佇列,和第一篇文章加入佇列的目的是一樣的,也是方便用來取消任務。
executorService().execute(call);//用執行緒池執行
} else {
//如果爆滿,就放到準備佇列
readyAsyncCalls.add(call);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
嘿嘿,果然和我們想的沒有錯,的確是使用了執行緒池:
public synchronized ExecutorService executorService() {
if (executorService == null) {
//配置的執行緒池。
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
小吐槽…這樣寫好像有點影響效能的樣子。通過synchronized實現單例模式。 還不如使用雙重判斷鎖的方式來實現,可以提升一定的效能,也避免synchronized開銷啊!!!
至此,我們已經完成了enqueue的分析了! 如果看懂了上篇文章的execute,看懂這個真的只是分分鐘的事情了!!
-Hans 2016.4.3 22:05
轉自:http://blog.csdn.net/qq_18402085/article/details/51052350
相關文章
- 【OkHttp3原始碼分析】(一)Request的executeHTTP原始碼
- OkHttp3原始碼分析[DiskLruCache]HTTP原始碼
- OkHttp3原始碼分析[綜述]HTTP原始碼
- okhttp3 攔截器原始碼分析HTTP原始碼
- OkHttp3原始碼分析[快取策略]HTTP原始碼快取
- OkHttp3原始碼分析[任務佇列]HTTP原始碼佇列
- OkHttp3原始碼分析[複用連線池]HTTP原始碼
- ThinkPHP6 原始碼分析之解析 RequestPHP原始碼
- ApiView/Request類原始碼分析/序列化器APIView原始碼
- MyBatis原始碼分析(二)MyBatis原始碼
- 原始碼分析二:LeakCanary原始碼
- Backbone原始碼分析(二)原始碼
- YYCache 原始碼分析(二)原始碼
- 原始碼|jdk原始碼之HashMap分析(二)原始碼JDKHashMap
- redis原始碼分析(二)、redis原始碼分析之sds字串Redis原始碼字串
- Spring原始碼系列(二)--bean元件的原始碼分析Spring原始碼Bean元件
- LinkedList原始碼分析(二)原始碼
- Volley原始碼分析(二)原始碼
- drf快速使用 CBV原始碼分析 drf之APIView分析 drf之Request物件分析原始碼APIView物件
- OkHttp3原始碼解析(一)之請求流程HTTP原始碼
- Retrofit原始碼分析二 代理模式原始碼模式
- Tinker接入及原始碼分析(二)原始碼
- SlidingMenu原始碼分析(二)原始碼
- ThinkPHP6 原始碼閱讀(二):Request 類是如何例項化的PHP原始碼
- OkHttp3原始碼解析(三)——連線池複用HTTP原始碼
- Android OkHttp3原始碼詳解——整體框架AndroidHTTP原始碼框架
- Vue原始碼分析系列二:$mount()方法Vue原始碼
- 窺探React-原始碼分析(二)React原始碼
- Zookeeper原始碼分析(二) —– zookeeper日誌原始碼
- Netty原始碼分析--Reactor模型(二)Netty原始碼React模型
- Kafka原始碼分析(二) - 生產者Kafka原始碼
- Spring原始碼分析之IoC(二)Spring原始碼
- Kubernetes Deployment 原始碼分析(二)原始碼
- Zookeeper原始碼分析(二) ----- zookeeper日誌原始碼
- Android Loader原始碼分析(二)Android原始碼
- MPTCP 原始碼分析(二) 建立子路徑TCP原始碼
- JUnit原始碼分析(二)——觀察者模式原始碼模式
- Go 互斥鎖 Mutex 原始碼分析(二)GoMutex原始碼