在OkHttp3中腰根,其靈活性很大程度上體現(xiàn)在可以攔截 其任意一個(gè)環(huán)節(jié)激才,而這個(gè)優(yōu)勢(shì)便是okhttp3整個(gè)請(qǐng)求響應(yīng)架構(gòu)體系的精髓所在,先放出一張主框架請(qǐng)求流程圖额嘿,接著再分析源碼
OKHttp簡(jiǎn)單用例
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.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());
}
});
首先我們來(lái)看OkHttpClient的部分源碼
public OkHttpClient() {
this(new Builder());
}
Builder是其內(nèi)部靜態(tài)類(lèi)瘸恼,它的構(gòu)造方法如下
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
我們?cè)賮?lái)看newCall方法中發(fā)生了什么
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
我們調(diào)用Call的enqueue、execute其實(shí)都是最終調(diào)用RealCall 的enqueue册养、execute东帅,下面我們來(lái)看它的源碼實(shí)現(xiàn)
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
這里可以看到最后都會(huì)調(diào)用OkHttpClient實(shí)例中初始化的Dispatcher實(shí)例里的enqueue、executed捕儒。下面我們著重說(shuō)明Dispatcher是什么以及有什么作用
Dispatcher
Dispatcher是一個(gè)任務(wù)調(diào)度器冰啃,它的主要作用有如下幾點(diǎn):
- 控制異步請(qǐng)求最大并發(fā)數(shù)和同一主機(jī)的請(qǐng)求連接數(shù)
- 執(zhí)行同步和異步請(qǐng)求的分發(fā)
- 執(zhí)行同步和異步請(qǐng)求的結(jié)束處理
Dispatcher部分源碼如下:
public final class Dispatcher {
//請(qǐng)求默認(rèn)最大并發(fā)數(shù)
private int maxRequests = 64;
//同一主機(jī)最大連接數(shù)
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** 異步請(qǐng)求線程池 */
private @Nullable ExecutorService executorService;
/** 異步請(qǐng)求準(zhǔn)備隊(duì)列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** 異步請(qǐng)求運(yùn)行時(shí)隊(duì)列 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** 同步請(qǐng)求運(yùn)行隊(duì)列*/
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
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;
}
.....
.....
}
控制異步請(qǐng)求最大并發(fā)數(shù)和同一主機(jī)的請(qǐng)求連接數(shù)
從上面RealCall 的異步請(qǐng)求源碼可以看到請(qǐng)求時(shí),實(shí)例化了一個(gè)AsyncCall
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
AsyncCall是RealCall的一個(gè)內(nèi)部類(lèi)刘莹,其源碼如下
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;
}
/**
* Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
@Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
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) {
e = timeoutExit(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);
}
}
}
NamedRunnable 這里不做過(guò)多的介紹阎毅,你只需要只要這是一個(gè)實(shí)現(xiàn)了Runnable接口的抽象類(lèi)即可。
下面我們來(lái)看Dispatcher中enqueue方法源碼
void enqueue(AsyncCall call) {
synchronized (this) {
//加入準(zhǔn)備隊(duì)列
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//是否超過(guò)最大并發(fā)數(shù)
if (runningAsyncCalls.size() >= maxRequests) break;
// 判斷同一主機(jī)是否大于的指定最大連接數(shù)
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue;
//如果滿(mǎn)足執(zhí)行條件点弯,從準(zhǔn)備隊(duì)列中移除
i.remove();
//添加到異步請(qǐng)求運(yùn)行隊(duì)列中
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
//判斷運(yùn)行隊(duì)列中是否還有任務(wù)
isRunning = runningCallsCount() > 0;
}
//遍歷異步請(qǐng)求隊(duì)列扇调,加入異步請(qǐng)求線程池中運(yùn)行
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
到了這邊就可以看到了Dispatcher的第一個(gè)功能,控制異步請(qǐng)求最大并發(fā)數(shù)和同一主機(jī)的請(qǐng)求連接數(shù)
執(zhí)行異步請(qǐng)求和同步請(qǐng)求的分發(fā)
從上面異步請(qǐng)求的源碼可以分析出Dispatcher異步請(qǐng)求分發(fā)的流程抢肛,如下:
- RealCall 的enqueue方法中生成一個(gè)AsyncCall異步任務(wù)
- Dispatcher執(zhí)行enqueue方法把AsyncCall異步任務(wù)加入到準(zhǔn)備隊(duì)列中
- 循環(huán)準(zhǔn)備隊(duì)列狼钮,判斷異步請(qǐng)求運(yùn)行隊(duì)列是否大于最大并發(fā)數(shù),大于則直接終止流程捡絮,等待異步請(qǐng)求運(yùn)行隊(duì)列空閑時(shí)再次觸發(fā)執(zhí)行熬芜。
- 小于最大并發(fā)數(shù),則判斷異步請(qǐng)求運(yùn)行隊(duì)列同一主機(jī)的連接數(shù)是否大于預(yù)設(shè)值福稳,大于跳過(guò)此次請(qǐng)求任務(wù)涎拉,繼續(xù)從準(zhǔn)備隊(duì)列中循環(huán)下一個(gè)請(qǐng)求任務(wù)。
- 異步請(qǐng)求運(yùn)行隊(duì)列同一主機(jī)的連接數(shù)小于預(yù)設(shè)值,則把AsyncCall異步任務(wù)從準(zhǔn)備隊(duì)列中刪除鼓拧,并加入異步請(qǐng)求運(yùn)行隊(duì)列中等待執(zhí)行
下面我們?cè)賮?lái)深入RealCall 中同步請(qǐng)求execute方法的源碼:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
可以看到這邊則非常簡(jiǎn)單半火,直接把同步任務(wù)加入同步請(qǐng)求隊(duì)列runningSyncCalls中
執(zhí)行同步和異步請(qǐng)求的結(jié)束處理
從上訴promoteAndExecute方法中可以看到異步任務(wù)都會(huì)走AsyncCall 的executeOn方法,而executeOn最終會(huì)執(zhí)行到自身的execute方法季俩,而無(wú)論execute執(zhí)行成功與否都會(huì)執(zhí)行Dispatcher的finished钮糖。
從上面RealCall 中的同步方法可以看到最后都會(huì)執(zhí)行Dispatcher的finished。那么我們就有必要對(duì)這個(gè)方法進(jìn)行進(jìn)一步的剖析
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
可以看到Dispatcher的finished方法中最后又會(huì)調(diào)到promoteAndExecute酌住,前面分析了Dispatcher異步請(qǐng)求流程店归,如果循環(huán)異步請(qǐng)求準(zhǔn)備隊(duì)列,異步請(qǐng)求循環(huán)隊(duì)列大于最大并發(fā)數(shù)或者同一主機(jī)的連接數(shù)大于預(yù)設(shè)值赂韵。則放棄處理娱节,直至異步請(qǐng)求運(yùn)行隊(duì)列空閑時(shí)再次觸發(fā)。而怎么觸發(fā)祭示,就是在這邊觸發(fā)的。
同時(shí)這個(gè)函數(shù)還會(huì)返回一個(gè)結(jié)果谴古,告知是否有請(qǐng)求任務(wù)正在運(yùn)行质涛。判斷是否有運(yùn)行中隊(duì)列通過(guò)以下方法實(shí)現(xiàn)
public synchronized int runningCallsCount() {
//返回同步請(qǐng)求隊(duì)列和異步請(qǐng)求隊(duì)列總和
return runningAsyncCalls.size() + runningSyncCalls.size();
}
如果沒(méi)有任務(wù)正在運(yùn)行,并且設(shè)置了idleCallback回調(diào)掰担,則會(huì)觸發(fā)idleCallback回調(diào)汇陆,觸發(fā)后續(xù)的操作。
OkHttp框架的攔截方法
就如文章一開(kāi)頭那張總圖概括带饱,無(wú)論是同步請(qǐng)求或者異步請(qǐng)求毡代,通過(guò)上面的源碼分析,可以發(fā)現(xiàn)最后都會(huì)走一個(gè)叫g(shù)etResponseWithInterceptorChain的方法勺疼。下面我們來(lái)看看這個(gè)方法的實(shí)現(xiàn)
Response getResponseWithInterceptorChain() throws IOException {
// 攔截器鏈
List<Interceptor> interceptors = new ArrayList<>();
//用戶(hù)自定義的攔截器
interceptors.addAll(client.interceptors());
//報(bào)錯(cuò)重試教寂、重定向攔截
interceptors.add(retryAndFollowUpInterceptor);
//橋接攔截器,橋接應(yīng)用層與網(wǎng)絡(luò)層执庐,添加必要的頭
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//緩存攔截器
interceptors.add(new CacheInterceptor(client.internalCache()));
//連接攔截器
interceptors.add(new ConnectInterceptor(client));
//從這就知道酪耕,通過(guò)okHttpClient.Builder#addNetworkInterceptor()傳進(jìn)來(lái)的攔截器只對(duì)非網(wǎng)頁(yè)的請(qǐng)求生效
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
//請(qǐng)求攔截器,這里執(zhí)行真正的網(wǎng)絡(luò)請(qǐng)求
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
這里不做具體攔截器的源碼分析轨淌,感興趣的同學(xué)自己去深入