Android網路框架:OKHttp原始碼簡單解析(一)
這是第一次解析原始碼並把它寫出來,在之前,我一直以為只要會用別人的輪子就好,做出實際的效果就行,對看原始碼對自己的能力提升不以為然。後來偶然聽到一句話:看別人的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;//最大併發請求數為64
private int maxRequestsPerHost = 5;//每個主機最大請求數為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進行快取等待。