之前一直在使用volley作爲網絡請求模塊,但發現volley已經過時(源碼還是很值得反覆品味的),現在基本是Retrofit和OkHttp的天下了,於是把OkHttp拿來學習一下。
首先,看一個OkHttp的簡單示例:
Request.Builder builder = new Request.Builder().url(url).get();
Request okRequest = builder.build();
Call call = mOkHttpClient.newCall(okRequest);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
mHandler.post(new Runnable() {
@Override
public void run() {
listener.onFetchFinished(response.body().string());
}
});
}
});
該類框架都比較類似,創建一個請求及回調,然後加入請求隊列,後面就由框架來進行任務調度,和結果回調。具體的細節,後面使用時再慢慢看,現在先探索一下OkHttp的主路線。
首先, 把請求加入到任務隊列:
//okhttp3.RealCall.java
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
此方法就爲避免單個請求多次添加,
// Dispatcher.java
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
/** 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.host().equals(call.host())) result++;
}
return result;
}
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;
}
這個方法比較有意思,很明顯有兩個任務隊列,一個是正在執行的隊列(runningAsyncCalls)和一個等待隊列(readyAsyncCalls);不僅限制了最大併發請求的數量,還對同一個域名的併發請求做了限制,當然單個應用可能請求的域名都是一樣的,所以這個就得看實際情況了。
當前併發請求數量小於限制時,就把請求直接丟到線程池中執行;否則就把請求加入到等待隊列。在代碼中搜索readyAsyncCalls,看看等待隊列中的任務是什麼時候被執行的。看到下面的方法:
// Dispatcher.java
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.
}
}
搜索這個方法是在哪裏被調用的:
可以看到,在任務結束、改變最大併發請求和改變域名最大併發請求時會調用該方法,這些肯定是在意料之中的。
接下來就跟蹤一下任務是如何執行的,我們就從隊列中的請求任務入手。
AsyncCall的繼承關係如下:
abstract class NamedRunnable implements Runnable
final class AsyncCall extends NamedRunnable
/**
* Runnable implementation which always sets its thread name.
*/
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();
}
任務的執行放在了execute()中執行。我們來看看AsyncCall的execute()方法:
@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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
可以看出來,主要的內容是在getResponseWithInterceptorChain()裏面,因爲這個方法直接返回了Response,也就是請求結果。
private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
這個方法的主要內容是得到一個列表interceptors,從字面上看,這個列表是用於請求攔截。通過斷點跟蹤後發現,這個列表確實重要,並且順序也是非常的重要,從下面的代碼慢慢分析。
//RealInterceptorChain.java
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpStream, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream, Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
、、、
// Call the next interceptor in the chain.
//注意這裏的index+1了
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
、、、
return response;
}
省略一些異常情況處理後,很清晰的看到,從interceptors列表的第一項(index初始化爲0)開始執行interceptor.intercept(next),並且參數是RealInterceptorChain鏈條的下一個節點。
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()));
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
、、、
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder().body(null).build())
.build();
}
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
、、、
request = followUp;
priorResponse = response;
} // end while
}
這個方法的主體是一個死循環,有兩種方式退出循環:一個是throw new IOException(“Canceled”),另一個是return response。也就是說,當請求被取消,或者得到結果了就結束返回了。再看看response是什麼時候被賦值的:
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
又回到剛纔的proceed(request)裏面了,而proceed(request)裏面用到了index+1,也就是這時會使用interceptors列表中的第二個interceptor來進行intercept(next),醬紫看來,這是一個遞歸的過程。只有當進行到chain中的某一個節點,能夠得response而不是繼續向下傳遞時,遞歸開始反轉退棧。
跟蹤代碼看到interceptor的順序爲RetryAndFollowUpInterceptor——>BridgeInterceptor——>CacheInterceptor——>ConnectInterceptor——>CallServerInterceptor. 至於爲何需要這麼幾層,還不是很清楚,還需要繼續學習。
至此,okhttp的請求主流程基本介紹完畢,後面還有大量的東西需要去學習。