本文使用okhttp3源碼來講解
1、同步和異步的差別
(1)同步方法execute()
okHttpBuilder.build().newCall()會建立一個RealCall對象
//OkHttpClient.java
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
然後看下RealCall的execute()方法
//RealCall.java
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");//如果任務已執行,則報異常
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this); //添加到雙向連結清單
Response result = getResponseWithInterceptorChain(); //執行請求
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this); //執行結束後從雙向連結清單移除
}
}
(2)異步方法enqueue
異步方法enqueue也位于RealCall.java中,具體實作如下
//RealCall.java
@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對象,并将回調接口作為參數。AsyncCall繼承于抽象類NamedRunnable,NamedRunnable又繼承自Runnable接口,是以這裡新開了一個線程去執行execute()方法。
//NamedRunnable.java
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();
}
//RealCall.java
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() { //繼承自父類,并在子線程run方法中被調用
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);
}
}
}
看下client.dispatcher().enqueue做了啥
//Dispatcher.java
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
enqueue将任務添加到雙端連結清單中,這裡有兩個隊列-任務隊列和等待隊列,當并發任務數大于64或者相同域名數量大于5個時就加入到等待隊列
2、攔截器種類和順序
使用okhttp進行網絡請求時不管是不同請求(execute())還是異步請求(enqueue(Callback responseCallback))都會走到getResponseWithInterceptorChain()方法來添加攔截器。
//RealCall.java
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); //使用者自定義攔截器
interceptors.add(retryAndFollowUpInterceptor); //重試和重定向攔截器
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, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis()); //第5個參數index=0要注意,後面分析會用到
return chain.proceed(originalRequest);
}
根據代碼可以看到攔截器添加的順序依次為client.interceptors()、retryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、networkInterceptors、CallServerInterceptor,由于是ArrayList容器是以可以保證順序。
看下攔截鍊的proceed方法,可以看到根據index參數以及RealInterceptorChain,每個攔截器都會執行其intercept方法,在intercept方法中會通過執行後續攔截鍊的proceed方法,這樣就逐漸執行了攔截連上的所有攔截器
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError(); //初始時index 是上面傳入的第五個參數0
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.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.httpCodec != 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, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout); //從下個攔截器開始的攔截鍊,初始時由于index是0,則index+1等于1,相當于下一個攔截器的index就變為了1,一次類推index逐漸增加
Interceptor interceptor = interceptors.get(index);//目前攔截器
Response response = interceptor.intercept(next);//以新的攔截鍊為參數,執行目前攔截器的intercept方法,這樣就一次執行了攔截鍊上的每個攔截器
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != 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;
}
網上盜個圖說下攔截器流程