前幾天在在一本書上看到這樣一句話
如果僅從微觀的視角關注每一個單獨的點,可能會因為看不到整體而迷失方向。
所以不會每一行代碼都去很細致地分析。因為抓住了整體再去看細節會比較輕松,而最初就一味追求細致可能會在不知不覺中放棄探索吧。
這篇的分析過程中忽略了InterceptorChain攔截器鏈的部分,在介紹完大概流程后單獨拿出來分析,下一篇再講。
OkHttp基本使用
來看源碼的話,OkHttp用起來應該已經很熟練了,我們從最基礎開始看它是如何實現的,直接上基本用法的代碼。
public class OkHttpTest {
private OkHttpClient mOkHttpClient;
public OkHttpTest() {
mOkHttpClient = new OkHttpClient();
mOkHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.MINUTES).build();
}
public void synRequest() {
Request newRequest = new Request.Builder()
.url("http://www.baidu.com")
.get()
.build();
Call call = mOkHttpClient.newCall(newRequest);
try {
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
public void asynRequest() {
Request newRequest = new Request.Builder()
.url("www.baidu.com")
.get()
.build();
Call call = mOkHttpClient.newCall(newRequest);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("onFailure");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("onSuccess");
System.out.println(response.body().string());
}
});
}
}
主要分為以下五個步驟:
- 構建OkHttpClient
- 構建Request
- 構建Call
- 執行call的execute()發送同步請求或enqueue()發送異步請求
- 對execute()返回的Response處理或enqueue()回調中的Response處理
以上為OkHttp基本使用方法,同步發送請求和異步發送請求的過程。
OkHttpClient的創建流程
OkHttpClient構造器
可以通過以下兩種方式創建OkHttpClient
mOkHttpClient = new OkHttpClient();
mOkHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.MINUTES).build();
進入OkHttpClient()中會發現無參數構造方法調用了一個參數的構造方法,并傳入了一個Builder對象。
public OkHttpClient() {
this(new Builder());
}
這里傳入一個Builder對象給OkHttpClient一個參數的構造器。
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
//...
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
//...
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
一個參數的構造器主要利用傳入的Builder對象初始了OkHttpClient的成員,并做了其他的一些初始工作。
那么我們先來看一下Builder到底是什么。
OkHttpClient的內部類Builder
這里的Builder是OkHttp的一個靜態內部類。
public static final class Builder {
//成員變量
Dispatcher dispatcher;
@Nullable Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
//...
//對成員變量賦初始值
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
//...
}
//使用參數okHttpClient來對成員變量賦值
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
//...
}
//下面是對成員變量參數的設置的方法
public Builder connectTimeout(long timeout, TimeUnit unit) {
connectTimeout = checkDuration("timeout", timeout, unit);
return this;
}
public Builder readTimeout(long timeout, TimeUnit unit) {
readTimeout = checkDuration("timeout", timeout, unit);
return this;
}
//...很多方法
//將此Builder對象傳入OkHttpClient構造器
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
可以看出來Builder對象實際存儲了很多參數變量,構造器對其初始化,內部提供改變參數的方法,方法返回本身使其可以鏈式調用
mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(5, TimeUnit.MINUTES)
.writeTimeout(10, TimeUnit.MINUTES)
.build()
最后調用build()將此包含處理過的參數的Builder對象傳給OkHttpClient的構造器,構造器中將此Builder對象給OkHttpClient成員變量賦值。
上述Builder內部類其實用到了一種設計模式,構建者模式。通過分析其代碼不難發現Builder的主要工作就是封裝了外部類需要的參數,并提供了一種比較方便的鏈式調用的方法去初始化一個類的成員變量,最后傳給外部類完成初始化得到外部對象。
創建Request對象
Request newRequest = new Request.Builder()
.url("http://www.baidu.com")
.get()
.build();
Request也是使用了Builder模式。
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tag = request.tag;
this.headers = request.headers.newBuilder();
}
可以看到默認的Request為Get請求。和OkHttpClient的方式很類似,就不多說了。
Call的創建
Call call = mOkHttpClient.newCall(newRequest);
將創建好的Request傳入client的newCall方法返回一個Call對象。
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
OkHttpClient實現了Call接口,newCall()是Call接口內部的Factory接口的抽象方法。
newCall調用的是Call實現類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.eventListener = client.eventListenerFactory().create(call);
return call;
}
構造器私有了,三個參數分別是傳入的client,傳入的原始request和一個是否為WebSocket的flag。
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
注意這里的retryAndFollowUpInterceptor,這個是之后攔截器鏈的第一個攔截器,之后會介紹到。
RealCall的execute()
Response response = call.execute();
真正的實現仍然是在RealCall中,來看RealCall的execute()。
@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);
}
}
我們來一點一點分析
首先判斷了此call對象是否被執行過,如果執行過就拋出異常,否則設置executed=true,繼續下面操作。這里synchronized包裹,表明call對象一定只能執行一次。
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
dispatcher()只是返回了client在Builder中初始的dispatcher成員變量,所以來看dispatcher的executed()。這里只做了一項工作,就是將call加入runningSyncCalls隊列中,這里留一個印象,只需要知道是將call加入了同步請求隊列中就可以了,在后面會細細來講Dispatcher類的,因為它還是比較重要的。
client.dispatcher().executed(this);
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
之前都一直是和request,到這里終于出現了reponse,也可以才想到,整個請求流程到這里應該是真正拿到了響應,是攔截器鏈的開端。
Response result = getResponseWithInterceptorChain();
來看getResponseWithInterceptorChain()的實現,將client中的攔截器和默認的攔截器加入集合中,將其傳入創建了一個RealInterceptorChain攔截器鏈chain。最后返回了chain的proceed()的返回值,暫時只需要知道它傳入了原始的request,返回了response,具體實現是在RealInterceptorChain這個類中的,我們在專門講攔截器時再去研究。
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());
return chain.proceed(originalRequest);
}
回到RealCall的execute()中來,在最后的finally中有這樣一行代碼,必須執行dispatcher的finished(),又調用了三個參數的finshed(),再一次看到runningSyncCalls這個隊列。記得在開始時我們將call加入到了這個隊列中,現在已經請求完畢,猜想應該要從隊列中刪除這個call對象。
client.dispatcher().finished(this);
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
果然在同步代碼塊中就有移除call的指令。其他操作是關于dispatcher的,我們在后面再去講。
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();
}
}
到一個GET請求就完成了。
RealCall的equeue()
發送一個異步請求其他步驟都是一樣的,唯獨這一步不一樣,我們從RealCall的equeue()開始分析。
@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));
}
首先也是判斷是否執行過,之后調用了dispatcher的equeue(),不同的是這里有一個參數,將傳入的responseCallback這個回調接口封裝成了AsyncCall對象。
AsyncCall是RealCall的一個靜態內部類,繼承了NamedRunnable,NamedRunnable實現了Runnable。來看NamedRunnable的run(),調用了execute(),但是并沒有實現,它在RealCall中的AsyncCall中實現了,看類聲明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的execute(),有沒有一點似曾相識的感覺。和RealCall的executed()一樣也調用了getResponseWithInterceptorChain()獲得response,之后進行一些回調操作。最后同樣調用finished(),將其移除隊列。那么問題來了,Call是在哪里加入隊列的?又是如何實現異步的呢?
@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);
}
}
我們回到client.dispatcher().enqueue(new AsyncCall(responseCallback));
這句話來看看dispatcher的equeue()都做了哪些工作,又為何能夠實現異步。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
做了一個判斷將call加入runningAsyncCalls還是readyAsyncCalls。同步請求中只有的一個隊列,而這里有兩個,一個為等待隊列一個為執行隊列,正是這兩個隊列實現了異步。在滿足條件后加入到執行隊列的下一行代碼可以理解為線程池直接執行了這個call請求,不滿足條件則加入等待隊列等待調度。
還記得這個call請求嗎,它是AsyncCalld對象,最終會調用上面分析過的實現了NamedRunnable的executed()真正地發送請求。
Dispatcher類
通過上面對同步異步發送請求的分析,可以感覺到實現這兩者區別的重點是由Dispatcher來實現的,Dispatcher的代碼不多,主要是這樣的流程:
我們來分析代碼。
變量
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
Dspatcher中有這些成員,maxRequests是正在執行的異步請求個數的最大值,maxRequestsPerHost是每個Host正在請求的請求個數最大值。executorService是執行請求的線程池,之后三個隊列前兩個用于異步請求的準備隊列和執行隊列,最后一個是同步請求的執行隊列。
enqueue()
先從我們熟悉的開始。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
enqueue()在上面我們已經簡單的分析過一次了,是從realCalld的enqueue()調用的。這里的判斷條件就涉及到maxRequests和maxRequestsPerHost了。
runningAsyncCalls.size() < maxRequests
,控制了執行隊列中請求個數。
runningCallsForHost(call) < maxRequestsPerHost
控制Host的值,來看runningCallsForHost()代碼
/** Returns the number of running calls that share a host with {@code call}. */
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.get().forWebSocket) continue;
if (c.host().equals(call.host())) result++;
}
return result;
}
遍歷了執行隊列中所有的call,計算出除了webSocket以外所有與傳入參數call同一個Host的call的個數,和我們上面分析的一致。
executorService().execute(call);
滿足條件加入隊列后的執行操作。來看executorService()
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;
}
單例模式返回了一個ThreadPoolExecutor對象,是一個線程池。
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory
)
參數非常多:
- corePoolSize: 核心線程數,默認情況下核心線程會一直存活。
- maximumPoolSize: 線程池所能容納的最大線程數。超過這個數的線程將被阻塞。
- keepAliveTime: 非核心線程的閑置超時時間,超過這個時間就會被回收。
- unit: keepAliveTime的單位。
- workQueue: 線程池中的任務隊列。
- threadFactory: 線程工廠,提供創建新線程的功能。
corePoolSize設置為0表示一旦有閑置的線程就可以回收。容納最大線程數設置的非常大,但是由于受到maxRequests的影響,并不會創建特別多的線程。60秒的閑置時間。
finished()
三個重載,前面兩個都是調用第三個private,記不記得前面兩個都是在什么地方調用的?看參數和注釋應該也能記起來,第一個是異步最后一步調用,第二個是在同步最后一步調用。它們調用第三個finished()區別就在于第三個參數異步為true,同步為false。
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
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();
}
}
來分析三個參數的finished(),同步代碼塊從隊列中移除了此時已經完成的這個call,接著判斷如果第三個參數為true就執行promoteCalls(),來看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.
}
}
promoteCalls()起到一個調控的作用,來控制readyAsyncCalls中的call進入runningAsyncCalls的。首先如果runningAsyncCalls滿了或者readyAsyncCalls中沒有call了就不做任何操作直接返回。之后遍歷readyAsyncCalls中的call,如果call的host滿足maxRequestsPerHost限制,就將其從maxRequestsPerHost中移除,加入runningAsyncCalls并立即執行,繼續循環遍歷直到將runningAsyncCalls加滿。異步比同步會多一個調控的步驟promoteCalls()。
回到finished()代碼中來,下一步是計算runningCallsCount,是同步異步正在請求的call的總數。
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
之后將成員遍歷idleCallback賦值給局部變量,idleCallback是空閑回調,當正在請求總數為0并且idleCallback不為空就調用其run()。
至此,Dispatcher類中的大部分核心就都涉及到了,還會有小部分代碼在后面的分析中再提。
總結
最后來總結一下以上分析到的大致流程。
- 首先使用Builder模式創建并初始化了OkHttpClient和Request對象。
- 傳入request調用client的newCall()創建了一個RealCall對象,實際操作都是由它完成的。
- 如果是同步請求則調用call的executed():
- 判斷此call對象是否執行過,未執行過再繼續。
- 調用dispatcher的executed(),將call加入同步執行隊列。
- 調用getResponseWithInterceptorChain()初始化攔截器集合并生成攔截器鏈。
- 調用攔截器鏈的proceed()依次處理request并依次處理response最終返回。
- 調用dispatcher的finished()將call移除隊列。
- 如果是異步請求則調用call的enqueue():
- 判斷此call對象是否執行過,未執行過再繼續。
- 將傳入的callBack封裝進AsyncCall對象,它實際為一個Runnable,實現了run方法的一部分。
- 調用dispatcher的enqueue并將asyncCall對象傳入。
- 判斷asyncCall加入執行隊列還是等待隊列。
- 如果滿足條件加入了等待序列則從線程池中分配線程立即執行asyncCall。
- 調用asyncCall的executed()。
- 調用getResponseWithInterceptorChain()初始化攔截器集合并生成攔截器鏈。
- 調用攔截器鏈的proceed()依次處理request并依次處理response最終返回。
- 根據結果,調用asyncCall中callBack的回調方法。
- 調用dispatcher的finished()將此call移除running隊列,并對隊列重新調整分配。
- 對響應結果進行處理。