在本系列的上一篇文章中,我們學習了OkHttp的基本用法,體驗了這個網絡加載框架的強大功能,以及它非常簡便的API。還沒有看過上一篇文章的朋友,建議先去閱讀 網絡請求框架OkHttp3全解系列 - (一)OkHttp的基本使用 。
如果我們想要進行get請求,那麼使用少量的代碼就能實現,如下所示:
OkHttpClient httpClient = new OkHttpClient();
String url = "https://www.baidu.com/";
Request getRequest = new Request.Builder()
.url(url)
.get()
.build();
Call call = httpClient.newCall(getRequest);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
看起來很簡潔的代碼,實際是OkHttp在背後幫我們默默執行了成噸的工作。
知其然也要知其所以然。那麼這篇我們就來解析一下OkHttp的源碼,看看它在這些簡單用法的背後,到底執行了多麼複雜的工作。
請求的創建
以上面get請求的代碼步驟分析,那麼先分析OkHttpClient實例的創建。
在上一篇中,提到OkHttpClient實例化可以直接創建,也可以使用Builder建造者模式進行配置然後build()即可。直接創建其實就是使用了默認的配置。其構造方法如下:
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;//調度器,用於控制併發的請求。內部保存同步和異步請求的call,並使用線程池處理異步請求。
this.proxy = builder.proxy;//代理設置
this.protocols = builder.protocols;//http協議
this.connectionSpecs = builder.connectionSpecs;//連接配置
this.interceptors = Util.immutableList(builder.interceptors);//全局攔截器
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);//網絡攔截器
this.eventListenerFactory = builder.eventListenerFactory;//請求的監聽器的創建工廠
this.proxySelector = builder.proxySelector;//代理選擇器
this.cookieJar = builder.cookieJar;//cookie,默認沒有cookie:CookieJar.NO_COOKIES
this.cache = builder.cache;//網絡緩存設置
this.internalCache = builder.internalCache;//內部使用的緩存接口
this.socketFactory = builder.socketFactory;//socket工廠
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;//以上幾個是安全相關設置
this.connectionPool = builder.connectionPool;//連接池
this.dns = builder.dns;//域名解析系統
this.followSslRedirects = builder.followSslRedirects;//ssl重定向
this.followRedirects = builder.followRedirects;//重定向
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;//連接失敗時是否重試
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;//幾個超時設置
this.pingInterval = builder.pingInterval;//ping間隔時間
//攔截器不能是null
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
public static final class Builder {
...
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;
}
...
}
直接創建OkHttpClient實例,配置項就是Builder構造方法中默認值。 可見配置項是非常多的,包括上一篇中已經使用過的超時設置、攔截器。
接着看Request的創建,也是使用建造者模式:
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
public Builder get() {
return method("GET", null);
}
public Builder post(RequestBody body) {
return method("POST", body);
}
public Builder method(String method, @Nullable RequestBody body) {
if (method == null) throw new NullPointerException("method == null");
if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
if (body != null && !HttpMethod.permitsRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must not have a request body.");
}
if (body == null && HttpMethod.requiresRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must have a request body.");
}
this.method = method;
this.body = body;
return this;
}
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
注意到get()和post(RequestBody body)都是對method方法的包裝,method方法內部對請求方式和請求體進行了校驗,比如get請求不能有請求體、post請求必須要請求體等。其他比較好理解,不再贅述。
接着看HttpClient的newCall方法:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
跟進RealCall的newRealCall方法:
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.transmitter = new Transmitter(client, call);
return call;
}
可見HttpClient的newCall方法獲得Call實際是RealCall。RealCall就是準備執行的請求,是對接口Call的實現。其內部持有OkHttpClient實例、Request實例。並且這裏還創建了Transmitter給RealCall的transmitter賦值。
Transmitter意爲發射器,是應用層和網絡層的橋樑,在進行 連接、真正發出請求和讀取響應中起到很重要的作用,看下構造方法:
public Transmitter(OkHttpClient client, Call call) {
this.client = client;
this.connectionPool = Internal.instance.realConnectionPool(client.connectionPool());
this.call = call;
this.eventListener = client.eventListenerFactory().create(call);
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
Transmitter內部持有OkHttpClient、連接池、call、事件監聽器。
再回頭看RealCall實現的接口Call:
// 已準備要執行的請求。由於表示單個請求/響應對(流),因此無法執行兩次
public interface Call extends Cloneable {
...
//同步請求,會阻塞
Response execute() throws IOException;
//異步請求
void enqueue(Callback responseCallback);
//取消請求,已經完成的不能取消。
void cancel();
boolean isExecuted();
boolean isCanceled();
Timeout timeout();
...
interface Factory {
Call newCall(Request request);
}
}
主要是定義請求的執行動作和狀態。RealCall對Call的具體實現,在後面執行流程中說明。
好了,請求的創建就到這裏了。
請求的調度
執行分爲同步和異步,這裏先從 同步請求 開始分析,即RealCall的execute方法:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();//超時計時開始
transmitter.callStart();//回調請求監聽器的請求開始
try {
client.dispatcher().executed(this);//放入隊列
return getResponseWithInterceptorChain();//執行請求獲取結果
} finally {
client.dispatcher().finished(this);//請求結束
}
}
首先判斷 如果已經執行,就會拋出異常。這就是一個請求只能執行一次的原因。然後回調請求監聽器的請求開始。然後調用client的調度器Dispatcher的executed方法:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
很簡單,請求放入一個雙端隊列runningSyncCalls中,表示正在執行的同步請求。
然後返回了getResponseWithInterceptorChain()的結果Response,可以猜到,同步請求真正的請求流程是在getResponseWithInterceptorChain方法中。
最後請求結束,會走Dispatcher的finished(Deque calls, T call)方法,:
//結束 異步請求
void finished(AsyncCall call) {
//callsPerHost-1
call.callsPerHost().decrementAndGet();
finished(runningAsyncCalls, call);
}
//結束 同步請求
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
//異步、同步的結束,都會走到這裏:從running中移除 並 調用promoteAndExecute
private <T> void finished(Deque<T> calls, T call) {
...
synchronized (this) {
//從隊列中移除
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
...
}
從隊列中移除call,然後執行了 promoteAndExecute(),這裏先不跟進去了後面會講到。
到這裏,我們知道了,同步請求走的是getResponseWithInterceptorChain()方法;
我們再來看 異步請求,即RealCall的enqueue方法:
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();//回調請求監聽器的請求開始
client.dispatcher().enqueue(new AsyncCall(responseCallback));//請求調度
}
同樣先判斷是否已請求過,回調請求開始。然後調用Dispatcher的enqueue方法,參數接受的是AsyncCall,AsyncCall繼承NamedRunnable,NamedRunnable實現自Runnable,即AsyncCall就是個Runnable,可以想到它是會在線程或線程池執行run方法的。run方法在AsyncCall沒看到啊,實際是在在NamedRunnable中:
//知道當前線程名字的Runnable
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
run調用 抽象方法execute(),execute()在AsyncCall是有實現的,這裏先不看。
我們繼續去看Dispatcher的enqueue方法:
void enqueue(AsyncCall call) {
synchronized (this) {
//存入等待執行的隊列
readyAsyncCalls.add(call);
// 相同host的請求,共用一個 調用計數
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
//從 runningAsyncCalls或者readyAsyncCalls中找到相同host的請求
private AsyncCall findExistingCallWithHost(String host) {
for (AsyncCall existingCall : runningAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
for (AsyncCall existingCall : readyAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
return null;
}
先把請求放入雙端隊列readyAsyncCalls中,表示等待執行的異步請求。爲啥是等待執行呢?先留一個疑問。 接着從 正在執行的請求runningAsyncCalls 或 等待執行的請求readyAsyncCalls 中找到是相同host的請求,把callsPerHost重用給當前請求。callsPerHost看名字感覺像是 擁有相同host的請求的數量,並且注意到類型是AtomicInteger,聲明如下:
private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
所以,相同host的請求是共享callsPerHost的,爲了後面判斷host併發做準備。
繼續看,接着調用了promoteAndExecute(),前面看的finish方法也有調用,這裏可以跟進看看了:
//調度的核心方法:在 控制異步併發 的策略基礎上,使用線程池 執行異步請求
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();
if (runningAsyncCalls.size() >= maxRequests) break; //最大併發數64
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; //Host最大併發數5
i.remove();//從等待隊列中移除
asyncCall.callsPerHost().incrementAndGet();//Host併發數+1
executableCalls.add(asyncCall);//加入 可執行請求 的集合
runningAsyncCalls.add(asyncCall);//加入 正在執行的異步請求隊列
}
isRunning = runningCallsCount() > 0;//正在執行的異步/同步 請求數 >0
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());//可執行的請求
}
return isRunning;
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
遍歷readyAsyncCalls,先進行兩個檢查:正在執行的異步請求runningAsyncCalls數量大於最大併發請求數64就break,或者 相同host請求的數量大於5,就continue。如果檢查都通過,就從等待隊列中移除,callsPerHost自增1,放入 可執行的集合executableCalls,並添加到隊列runningAsyncCalls中,表示正在執行的異步請求。
這裏就解釋了 異步請求等待隊列的意義了,就是爲了控制最大併發數的緩衝:異步請求併發數達到64、相同host的異步請求達到5,都要放入等待隊列。
遍歷完後 把executableCalls中的請求都走executeOn方法:
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);//在線程池執行asyncCall
success = true;
} catch (RejectedExecutionException e) {
...
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);//回調失敗
} finally {
if (!success) {
client.dispatcher().finished(this); //執行發生異常,結束
}
}
}
//線程池的定義
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
executeOn方法很簡單:使用類似CachedThreadPool的線程池 執行請求RealCall。如果執行失敗,也會調用dispatcher的finished(Deque calls, T call)方法。
前面分析過,AsyncCall的run方法會走到execute()方法,來看下:
protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();//超時計時開始
try {
Response response = getResponseWithInterceptorChain();////執行請求獲取結果
responseCallback.onResponse(RealCall.this, response);//回調結果
} catch (IOException e) {
...
responseCallback.onFailure(RealCall.this, canceledException);//回調失敗
...
} finally {
client.dispatcher().finished(this);//請求結束
}
}
我們發現,這裏和 同步請求 就很像了,同樣是調用getResponseWithInterceptorChain()方法來獲取結果Response,不同點是使用responseCallback把結果回調出去,最後請求結束也是調用了dispatcher的finish方法。
另外,前面說過,finish方法中也調用了promoteAndExecute()方法,說明 同步/異步 請求 結束後 也會重新調度當前的異步請求。
好了,到這裏我們把 調度流程 梳理下:
- 發起 同步 請求後,RealCall使用Dispatcher存入runningSyncCalls,然後使用getResponseWithInterceptorChain()獲取結果,最後調用Dispatcher的finish方法結束請求。
- 發起 異步 請求後,RealCall使用Dispatcher存入readyAsyncCalls,獲得host併發數,使用promoteAndExecute()方法 在 控制異步併發 的策略基礎上,使用 線程池 執行異步請求(併發控制有包括 最大併發數64、host最大併發數5)。異步請求的執行 也是使用getResponseWithInterceptorChain(),獲得結果後回調出去。最後調用Dispatcher的finish方法結束請求。
- Dispatcher:調度器,主要是異步請求的併發控制、把異步請求放入線程池執行,實現方法是promoteAndExecute()。 promoteAndExecute()有兩處調用:添加異步請求時、同步/異步請求 結束時。
請求的執行
重點來啦!
通過上面分析指導,無論同步還是異步請求,最終的執行都是在RealCall的getResponseWithInterceptorChain()方法,只不過異步請求 需要先通過Dispatcher進行併發控制和線程池處理。那麼就來看看getResponseWithInterceptorChain():
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); //使用者配置的 應用攔截器,最先攔截
interceptors.add(new RetryAndFollowUpInterceptor(client));//重試跟進攔截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));//橋攔截器
interceptors.add(new CacheInterceptor(client.internalCache()));//緩存攔截器
interceptors.add(new ConnectInterceptor(client)); //連接攔截器
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors()); //使用者配置的網絡攔截器
}
interceptors.add(new CallServerInterceptor(forWebSocket)); //請求服務攔截器
//攔截器鏈
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);//鏈 開始執行
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
首先是 把
- 應用攔截器(外部配置)client.interceptors()、
- 重試跟進攔截器RetryAndFollowUpInterceptor、
- 橋攔截器BridgeInterceptor、
- 緩存攔截器CacheInterceptor、
- 連接攔截器ConnectInterceptor、
- 網絡攔截器(外部配置)client.networkInterceptors()、
- 請求服務攔截器CallServerInterceptor,
依次 添加到集合interceptors中。然後使用interceptors、transmitter、originalRequest等創建了攔截器鏈RealInterceptorChain實例,最後用proceed方法獲取到請求的結果Response。
在上一篇 使用方法中有提到攔截器Interceptor,那裏配置的攔截器 實際就是 應用攔截器:client.interceptors(),是最早被添加到interceptors中。那麼到底 攔截器是個啥呢?chain.proceed是如何獲取到結果的呢?不着急,我們先看看Interceptor類:
//攔截器
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
//攔截器鏈
interface Chain {
Request request();
//Chain的核心方法
Response proceed(Request request) throws IOException;
//返回請求執行的 連接. 僅網絡攔截器可用; 應用攔截器就是null.
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
Interceptor是個接口類,只有一個intercept方法,參數是Chain對象。再注意到 內部接口類Chain – 攔截器鏈,有個proceed方法,參數是Request對象,返回值是Response,那麼這個方法的實現就是請求的處理過程了。Chain的唯一實現類就是RealInterceptorChain,負責把所有攔截器串聯起來,proceed方法就是串聯的操作。
上述一系列的攔截器都是Interceptor的實現類,這裏先貼出上一篇中實現的應用攔截器(其他攔截器的實現暫不去跟進):
new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
Log.i(TAG, "intercept: proceed start: url"+ url+ ", at "+System.currentTimeMillis());
Response response = chain.proceed(request);
ResponseBody body = response.body();
Log.i(TAG, "intercept: proceed end: url"+ url+ ", at "+System.currentTimeMillis());
return response;
}
}
在intercept方法中我們調用chain.proceed方法獲取了結果 並在前後打印了一些日誌,那這個Chain實例是哪來的呢?intercept方法啥時候被調用的呢?— — 我們再回頭看getResponseWithInterceptorChain方法,所有攔截器都被傳入RealInterceptorChain,可以猜想到,必定是RealInterceptorChain的proceed方法內部調用了攔截器的intercept方法。 那麼就來看看吧:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
在實例化RealInterceptorChain時 index賦值是0,exchange是null,所以前面三個if都沒走進去。然後獲取了第一個攔截器,也就是我們配置的應用攔截器,調用了它的interceptor方法,並返回和校驗了結果。這裏證實了我們猜想。同時注意到,調用 應用攔截器的interceptor方法傳入的參數:攔截器鏈實例next,next就是把index + 1而已,其他參數和當前實例是一樣的。也就是說 在我們的應用攔截器中調用的是 next的proceed方法。
進一步,next的proceed方法中 同樣會獲取interceptors的index=1的攔截器,即RetryAndFollowUpInterceptor實例,然後調用其interceptor方法,參數是index+1即index=2的chain。跟進RetryAndFollowUpInterceptor的代碼發現,interceptor方法內部也是有調用chain的proceed方法。這樣就會依次傳遞下去,直到最後一個攔截器CallServerInterceptor。
實際上 除了最後一個攔截器CallServerInterceptor之外,所有攔截器的interceptor方法都調用了 傳入 chain的proceed方法。每個攔截器在chain的proceed方法 前後 處理了自己負責的工作。例如我們的應用攔截器,在chain的proceed方法前 打印了request信息的日誌,chain的proceed方法獲取結果 之後 打印了response信息的日誌。每個攔截器interceptor方法在 調用chain的proceed方法時 都是爲了獲取下一個攔截器處理的response,然後返回給上一個攔截器。
邏輯總結如下圖:
這就是 okhttp執行流程的核心了,整體流程如下:
現在來總結下:
- 攔截器鏈:把原始請求 request 依次 傳入到 每個攔截器。攔截器 處理後 把response 反向 依次 回傳。
- 攔截器:可以對request進行處理,然後調用index+1的攔截器鏈proceed方法 獲取下一個攔截器處理的結果,接着自己也可以處理這個結果,即: 處理request、chain.proceed、處理response。
不知你有沒有發現,這一過程 和 公司工作生產流程 很像:
- 老闆接到一筆訂單,要求10天內生產100臺電腦。
- 總經理拿到任務後,修改了任務和時間:8天內生產110臺,這是基於 生產合格率 以及進行重工、檢驗、包裝、運輸的時間上的考量,既要保質保量,也要按時交貨。
- 任務接着到了部門經理,部門經理先去確認了倉庫中是否有足夠存貨,如果有就直接使用存貨來交貨,這樣不存在任何交貨風險(質量、時間);如果沒有存貨,那麼就去要求生產線生產。
- 生產線按時按量生產完以後,會把生產情況 上報給部門經理,部門經理把結果總結成excel呈現給總經理,總經理則會把整個生產流程結果及各部門的配合情況,總結成PPT報告給老闆。
而不同的攔截器,在網絡請求這一任務中,就扮演着不同的角色。可能okhttp的作者寫攔截器的靈感就來源於生活吧,哈哈。
攔截器 | 作用 |
---|---|
應用攔截器 | 處理原始請求和最終的響應:可以添加自定義header、通用參數、參數加密、網關接入等等。 |
RetryAndFollowUpInterceptor | 處理錯誤重試和重定向 |
BridgeInterceptor | 應用層和網絡層的橋接攔截器,主要工作是爲請求添加cookie、添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然後保存響應結果的cookie,如果響應使用gzip壓縮過,則還需要進行解壓。 |
CacheInterceptor | 緩存攔截器,獲取緩存、更新緩存。如果命中緩存則不會發起網絡請求。 |
ConnectInterceptor | 連接攔截器,內部會維護一個連接池,負責連接複用、創建連接(三次握手等等)、釋放連接以及創建連接上的socket流。 |
網絡攔截器 | 用戶自定義攔截器,通常用於監控網絡層的數據傳輸。 |
CallServerInterceptor | 請求攔截器,在前置準備工作完成後,真正發起網絡請求,進行IO讀寫。 |
這裏先大概知道每個攔截器的角色任務,下一篇將會詳細分析每個攔截器,以及重要知識點–緩存和連接池。
那麼,我們對okhttp執行流程的源碼分析,到這裏也結束了。
總結
現在通過兩篇文章,我們已經掌握了okhttp的基本用法,並且通過閱讀源碼瞭解了okhttp總的執行流程——請求的創建、調度、攔截器鏈處理。接下來的文章,會深入到每個攔截器的具體實現,學習okhttp更多的高級使用技巧。