OkHttp3源碼解析(一)分發(fā)器Dispatcher原理分析
OkHttp3源碼解析(二)五大攔截器原理分析
OkHttp 3.10.0版本,最新OkHttp為:4.0.1邏輯與3版本并沒有太大變化,但是改為kotlin實(shí)現(xiàn)烫止。
OkHttp介紹
OkHttp是當(dāng)下Android使用最頻繁的網(wǎng)絡(luò)請求框架几于,由Square公司開源话侧。Google在Android4.4以后開始將源碼中的HttpURLConnection底層實(shí)現(xiàn)替換為OKHttp,同時(shí)現(xiàn)在流行的Retrofit框架底層同樣是使用OKHttp的益老。
優(yōu)點(diǎn):
- 支持Spdy彪蓬、Http1.X、Http2捺萌、Quic以及WebSocket
- 連接池復(fù)用底層TCP(Socket)档冬,減少請求延時(shí)
- 無縫的支持GZIP減少數(shù)據(jù)流量
- 緩存響應(yīng)數(shù)據(jù)減少重復(fù)的網(wǎng)絡(luò)請求
- 請求失敗自動(dòng)重試主機(jī)的其他ip,自動(dòng)重定向
- …….
異步get請求
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默認(rèn)就是GET請求桃纯,可以不寫
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: ");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "onResponse: " + response.body().string());
}
});
異步Post請求
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
.add("search", "Jurassic Park")
.build();
Request request = new Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(requestBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, response.protocol() + " " +response.code() + " " + response.message());
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
Log.d(TAG, headers.name(i) + ":" + headers.value(i));
}
Log.d(TAG, "onResponse: " + response.body().string());
}
});
1酷誓、OkHttp的調(diào)用流程
OkHttp請求過程中最少只需要接觸OkHttpClient、Request态坦、Call盐数、Response,但是框架內(nèi)部進(jìn)行大量的邏輯處理伞梯。
所有的邏輯大部分集中在攔截器中玫氢,但是在進(jìn)入攔截器之前還需要依靠分發(fā)器來調(diào)配請求任務(wù)。
分發(fā)器:內(nèi)部維護(hù)隊(duì)列與線程池谜诫,完成請求調(diào)配漾峡;
攔截器:五大默認(rèn)攔截器完成整個(gè)請求過程。
2猜绣、分發(fā)器:異步請求的工作流程
Call call = client.newCall(request);
Response response = call.enqueue();
final class RealCall implements Call
=> RealCall#enqueue
=> client.dispatcher().enqueue(new AsyncCall(responseCallback));
=> Dispatcher#enqueue
當(dāng)我們調(diào)用client.dispatcher().enqueue(new AsyncCall(responseCallback));的時(shí)候會(huì)進(jìn)入Dispatcher的enqueue方法灰殴,
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
一開始會(huì)傳一個(gè)AsyncCall到dispatcher中,在dispatcher的enqueue里,AsyncCall可能會(huì)加入到正在運(yùn)行的runningAsyncCalls隊(duì)列或者加入到正在等待的readyAsyncCalls隊(duì)列牺陶,如果加入到runningAsyncCalls隊(duì)列的話伟阔,會(huì)被進(jìn)一步提交到ThreadPool線程池中,因此AsyncCall肯定是一個(gè)實(shí)現(xiàn)了Runnable接口的“任務(wù)”掰伸。當(dāng)這個(gè)任務(wù)在線程池中執(zhí)行完畢的時(shí)候皱炉,會(huì)調(diào)用dispatcher里的finished方法,在finished中又會(huì)調(diào)用到dispatcher里的promoteCalls方法狮鸭,去遍歷readyAsyncCalls隊(duì)列合搅,找出滿足條件的任務(wù),加入到runningAsyncCalls隊(duì)列里歧蕉,又開始新一輪的執(zhí)行灾部。這就是分發(fā)器里整個(gè)異步請求的工作流程。
那么問題來了:
- 1惯退、分發(fā)器dispatcher如何決定將請求放入runningAsyncCalls還是readyAsyncCalls赌髓?
- 2、從readyAsyncCalls移動(dòng)到runningAsyncCalls的條件是什么催跪?
- 3锁蠕、分發(fā)器dispatcher線程池是怎么工作的?
3懊蒸、源碼分析
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
當(dāng)正在請求的任務(wù)數(shù)量runningAsyncCalls小于maxRequests(異步請求同時(shí)存在的最大請求默認(rèn)64個(gè))荣倾,這個(gè)maxRequests數(shù)量也是可以通過setMaxRequests方法來設(shè)置的,但最小不能小于1個(gè)骑丸,不然會(huì)報(bào)異常舌仍。另外一個(gè)限制是:異步請求同一域名同時(shí)存在的最大請求runningCallsForHost不能大于maxRequestsPerHost(默認(rèn)是5個(gè)),當(dāng)然maxRequestsPerHost也是能通過setMaxRequestsPerHost來自己重新設(shè)置的通危。如果滿足這兩個(gè)條件抡笼,就會(huì)把這個(gè)AsyncCall也就是Runnable加入到runningAsyncCalls隊(duì)列中,同時(shí)加入到executorService線程池中黄鳍,并且調(diào)用execute開始執(zhí)行這個(gè)任務(wù)。
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;
}
進(jìn)入到這個(gè)線程池平匈,首先核心線程為0框沟,表示線程池不會(huì)一直為我們緩存線程,線程池中所有線程都是在60s內(nèi)沒有工作就會(huì)被回收增炭。而最大線程Integer.MAX_VALUE
與等待隊(duì)列SynchronousQueue
的組合能夠得到最大的吞吐量忍燥。即當(dāng)需要線程池執(zhí)行任務(wù)時(shí),如果不存在空閑線程不需要等待隙姿,馬上新建線程執(zhí)行任務(wù)梅垄!等待隊(duì)列的不同指定了線程池的不同排隊(duì)機(jī)制。一般來說输玷,等待隊(duì)列BlockingQueue
有:ArrayBlockingQueue
队丝、LinkedBlockingQueue
與SynchronousQueue
靡馁。那么為什么要使用SynchronousQueue
: 無容量的隊(duì)列。其實(shí)机久,使用此隊(duì)列意味著希望獲得最大并發(fā)量臭墨。因?yàn)闊o論如何,向線程池提交任務(wù)膘盖,往隊(duì)列提交任務(wù)都會(huì)失敗胧弛。而失敗后如果沒有空閑的非核心線程,就會(huì)檢查如果當(dāng)前線程池中的線程數(shù)未達(dá)到最大線程侠畔,則會(huì)新建線程執(zhí)行新提交的任務(wù)结缚。完全沒有任何等待,唯一制約它的就是最大線程數(shù)的個(gè)數(shù)软棺。因此一般配合Integer.MAX_VALUE
就實(shí)現(xiàn)了真正的無等待红竭。
回到之前的代碼,如果不滿足码党,加入到等待隊(duì)列readyAsyncCalls德崭。那么這個(gè)Runnable真正在哪里執(zhí)行它的run方法呢?
final class AsyncCall extends 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();
}
我們看到AsyncCall是繼承自NamedRunnable揖盘,NamedRunnable又實(shí)現(xiàn)了Runnable接口眉厨,在NamedRunnable的run方法里調(diào)用了execute方法,execute是個(gè)抽象方法兽狭,所以它真正實(shí)現(xiàn)的地方就是在AsyncCall的execute方法里
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@Override
protected void execute() {
boolean signalledCallback = false;
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) {
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);
}
}
}
所以憾股,我們先不去關(guān)心這個(gè)任務(wù)AsyncCall到底是怎么執(zhí)行的。我們看到最后也就是任務(wù)跑完了箕慧,不管成功或者失敗服球,一定會(huì)去執(zhí)行finally里的代碼塊client.dispatcher().finished(this)表示這個(gè)任務(wù)結(jié)束了。所以我們先去看看dispatcher里的finished到底是怎么執(zhí)行的颠焦。
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
一開始把call從calls里(也就是傳進(jìn)來的runningAsyncCalls)移除斩熊。然后執(zhí)行promoteCalls方法
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
一開始也是判斷maxRequests和maxRequestsPerHost,然后遍歷readyAsyncCalls隊(duì)列伐庭,把任務(wù)從readyAsyncCalls里取出加入到runningAsyncCalls里粉渠。到這里,分發(fā)器里整個(gè)異步請求的工作流程源碼分析完成了圾另。同步請求就不展開講了霸株,很簡單,收到任務(wù)時(shí)加入runningSyncCalls隊(duì)列集乔,執(zhí)行完畢從runningSyncCalls隊(duì)列移除去件。
最后,總結(jié)一下:
-
Q1: 如何決定將請求放入ready還是running?
如果當(dāng)前正在請求數(shù)不小于64放入ready尤溜;如果小于64倔叼,但是已經(jīng)存在同一域名主機(jī)的請求5個(gè)放入ready!
-
Q2: 從running移動(dòng)ready的條件是什么靴跛?
每個(gè)請求執(zhí)行完成就會(huì)從running移除缀雳,同時(shí)進(jìn)行第一步相同邏輯的判斷,決定是否移動(dòng)梢睛!
-
Q3: 分發(fā)器線程池的工作行為肥印?
無等待,最大并發(fā)