這是第一次解析源碼并把它寫出來,在之前,我一直以為隻要會用别人的輪子就好,做出實際的效果就行,對看源碼對自己的能力提升不以為然。後來偶然聽到一句話:看别人的DEMO,你就可以會用輪子,但是要想用好輪子,還是得看源碼。我覺得看源碼有兩個方面的好處:
1. 從本質上去了解所學架構的原理和流程;
2. 可以看到軟體開發裡常見的思維方法和設計模式;這些在程式設計裡都是相通的。
下面這篇文章,我們隊OkHttp架構做一個簡單的分析。
使用OkHttp通路網絡,通用的做法就是下面四步:
步驟:
//1 建立okHttpClient對象
//2 建立網絡Request對象
//3 建立與Request對應的傳回Call對象
//4 請求加入排程(直接執行)
在第四步中,通路網絡有兩種方式,一種是同步請求通路網絡。一種是異步請求通路網絡,我們先看看同步通路網絡是怎麼實作的。
同步通路網絡實作源碼解析:它是通過okHttpClient.newCall(re)建立一個Call對象。如下:
OkHttpClient okHttpClient =new OkHttpClient();
Response response = okHttpClient.newCall(request).execute();
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
Call call=okHttpClient.newCall(request);
Response response = call.execute();
if(response.isSuccessful()){ Log.i("TAG","response.code()="+response.code());//200 Log.i("TAG","response.body().string()="+response.body().string());
}
這裡我們先看看Call對象到底是什麼鬼?跟蹤源碼我們可以看到:
/**
* Prepares the {@code request} to be executed at some point in the future.//準備一個在未來某個點會被執行的網絡請求
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}
可以看到真正起作用的是RealCall,并非我們看到的Call;我們再看看RealCall是什麼?
final class RealCall implements Call {//RealCall是一個繼承了Call接口的網絡真正請求類,
private final OkHttpClient client;
....
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client;
this.originalRequest = originalRequest;
}
//真正執行execute的地方;
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
上面代碼中,client.dispatcher().executed(this);是将目前網絡請求,從OkHttpClient獲得的Dispatcher然後把它加入到分發器裡面的隊列 executedCalls中,在完成的時候會remove掉,源碼如下:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
synchronized void finished(Call call) {
if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
}
上面中的runningSyncCalls是指正在執行的同步任務隊列,在OkHttp架構中對網絡請求進行分發任務的是Dispatcher類,Dispatcher維護了如下變量,用于控制并發的請求
private int maxRequests = ;//最大并發請求數為64
private int maxRequestsPerHost = ;//每個主機最大請求數為5
private ExecutorService executorService;//線程池
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//維護準備執行的異步請求隊列,雙端隊列緩存(用數組實作,可自動擴容,無大小限制);
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在執行的異步任務隊列,包括已經取消但是并未finish的請求
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//正在執行的同步任務隊列,包括已經取消但是并未finish的請求
Dispatcher: 分發者,也就是生産者(預設在主線程)
AsyncCall: 隊列中需要處理的Runnable(包裝了異步回調接口)
ExecutorService:消費者池(也就是線程池)
根據生産者消費者模型的模型理論,當入隊(enqueue)請求時,如果滿足(runningRequests<64 && runningRequestsPerHost<5),那麼就直接把AsyncCall直接加到runningCalls的隊列中,并線上程池中執行。如果消費者緩存滿了,就放入readyAsyncCalls進行緩存等待。