okhttp原始碼解析(一)
一個處理網路請求的開源專案,是安卓端最火熱的輕量級框架,由移動支付Square公司貢獻,用於替代HttpUrlConnection和Apache HttpClient。
我將通過一系列的文章來分析okhttp的原始碼:
如何引入
在app的build.gradle檔案中
dependencies {
compile ‘com.squareup.okhttp3:okhttp:3.10.0’
}
這裡引入的3.10.0版本,該系列文章分析的原始碼均基於這個版本。
如何使用
- 同步請求
OkHttpClient client = new OkHttpClient.Builder ().build();
Request request = new Request.Builder().url("http://xxx").get().build();
Call call = client.newCall(request);
try {
call.execute();
} catch (IOException e) {
e.printStackTrace();
}
- 非同步請求
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url("http://xxx" ).get().build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException{
}
});
建立過程分析
通過上面同步和非同步get請求的使用方法,大致可以發現okhttp暴露給上層開發者使用的主要類OkHttpClient、Request和Call,當然不僅僅只是這些接下來我們就對這3個類進行分析
OkhttpClient的建立
OkHttpClient client = new OkHttpClient.Builder().build();
OkHttpClient.Builder, OkHttpClient的內部類,相信見到這個Builder大家都不會陌生,這種構建物件的方式就是設計模式中的建造者模式,當構建一個物件引數比較多,並且根據不同的引數實現不同的物件特徵的時候就會使用這種模式。建立過程不是我們分析的重點,感興趣的小夥伴可以去原始碼裡看一下。看過我文章的都知道我的原始碼分析都是配合我的靈魂畫技來展示的,下面就用UML類圖的方式來表現OkhttpClient類中比較重要的部分
OkHttpClient中比較重要的成員變數和方法都在這個圖中了,其中重要成員變數dispatcher,重要方法Call newCall(Request request)我們會在稍後的部分進行分析。
Request的建立
Request request = new Request.Builder().url("http://xxxx").get().build();
Request物件的建立跟OkHttpClient一樣都是建造者模式。Request物件內部就簡單的多了,就是封裝了請求需要的url、方法等。
Call的建立
Call call = client.newCall(request);
Call物件的建立是呼叫的OkHttpClient物件的newCall方法,傳入一個Request物件引數。接下來我們看一下OkHttpClient中的newCall方法。
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
newCall方法的返回值型別是一個Call,需要的入參是一個Request物件。
這裡呼叫了RealCall類中靜態方法newRealCall(),newRealCall()的返回值型別是一個Call。接下來我們到RealCall類中看一看具體實現
RealClall
//Call介面的實現類
final class RealCall implements Call {
//OkHttpClient物件
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
private EventListener eventListener;
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
//建構函式
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
//靜態方法,內部通過建構函式建立RealCall物件
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;
}
}
RealCall類中靜態方法newRealCall()的返回值型別是一個RealCall,內部通過建構函式構建了一個RealCall物件,並且我們發現Call原來是一個介面,而RealCall物件是Call的實現類。OkhttpClient物件呼叫newCall(request)方法實際獲得的是Call介面的實現類RealCall的物件。到這裡我們的3個類的建立過程都清晰了。
接下來我們簡單看一下RealCall類。
RealCall.AsyncCall
RealCall中有一個內部類AsyncCall,AsyncCall從名字就可以看出來和非同步相關,而AsyncCall中就有一個很重要的成員變數responseCallback,就是我們網路請求的回撥介面Callback,AsyncCall繼承自NamedRunnable,而NamedRunnable是抽象類,並且是Runnable介面的實現類,那麼我從這層關係就可以知道RealCall的內部類AsyncCall是一個執行緒類。好了暫時先看到這裡,這個AsyncCall後面會再次提到,當然RealCall中還有一些很重要的成員變數和方法,這些都交給我這個靈魂畫手吧~!
以上就是RealCall。
OK到此我們把OkhttpClient、Request和Call都分析了一下,這裡對於建立過程做一個總結:
1. OkHttpClient Builder方式建立。
2. Request Builder方式建立。
3. Call是一個介面,實現類是RealCall,RealCall是通過OkHttpClient 物件呼叫。
newCall()方法傳入Request 物件,內部呼叫的RealCall的靜態方法newRealCall()方法通過建構函式建立。
下面就是靈魂畫手最激動人心的時刻,將這三者的關係通過UML類圖表現出來。出來吧~叼毛獸~!
得到了Call物件我們就可以用這個物件來執行同步請求或者非同步請求了,從上面的分析來看Call是一個介面,那麼我們就去它的實現類RealCall中去看:
同步請求實際就呼叫了RealCall的execute();
非同步請求實際就呼叫了RealCall的enqueue(Callback responseCallback);
接下來就來分別分析上面的提到的兩個方法。
- 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);
}
}
以上就是execute()方法的內部邏輯,我們這裡只分析主幹線,第一個if判斷
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
表示請求只能執行一次,不然我分分鐘鍾拋個異常給你看。然後我們看try{}部分
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
}
程式碼也很簡單,try的邏輯也很簡單,主要的就是上面兩行程式碼,這裡分別執行了client.dispatcher().executed(this)方法和getResponseWithInterceptorChain()方法。
getResponseWithInterceptorChain()這個方法我們後續篇章會單獨去講解它,這裡面是okhttp中很重要的一個概念【攔截器】。
我們再來看finally{}部分。
finally {
client.dispatcher().finished(this);
}
可以發現無論是try部分還是最終的finally部分都出現了client.dispatcher(),都是呼叫client.dispatcher()所返回的物件的相應方法來進行處理,client我們自然不陌生就是OkHttpClient的物件。那麼它的dispatcher()方法返回了什麼呢? 還記得我們OkHttpClient的UML類圖嗎?不記得了我們可以翻回前面看一下,上文有提到OkHttpClient中的重要屬性和方法,前面newCall()方法我們已經分析過了,剩下的就是重要成員變數dispatcher,而client.dispatcher()返回的就是Dispatcher類在OkHttpClient中的成員變數dispatcher。
public Dispatcher dispatcher() {
return dispatcher;
}
到這裡我們的同步請求就把邏輯引入到Dispatcher類中。我們先不去看這個類,再回過頭來看看Call的非同步請求!
- enqueue(Callback responseCallback)
@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));
}
於同步請求相同的if判斷,這裡不在贅述,關鍵程式碼就一行
client.dispatcher().enqueue(new AsyncCall(responseCallback));
同樣的非同步請求也將邏輯引入到Dispatcher類中。那麼接下來我們就分析Dispatcher類,看看這個類在okhttp中是充當了什麼樣的角色,同步和非同步的請求都在這個類中做了些什麼。
Dispatcher的建立
這次我們先上圖
Dispatcher的建立離不開OkHttpClient,Dispatcher類是作為OkHttpClient物件的成員變數dispatcher 存在的,OkHttpClient在通過Builder()方式建立物件的時候就構建了Dispatcher物件,為成員變數dispatcher賦值。這裡我們擷取部分OkHttpClient的建立過程。
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
......//省略部分程式碼
}
接下來深入Dispathcer類,其中重要的成員變數和方法都在上面的圖中體現了,可以看到Dispathcer中有執行緒池物件,還有3個佇列,以及同步請求和非同步請求在RealCall中出現的方法execute()和enqueue(Callback responseCallback)。
其實從成員變數上我們大致可以看出這個類的重要性,執行緒池、佇列等等,我們都知道Android中網路請求都是在子執行緒中進行,開闢新執行緒是是否耗費資源的事情,一般需要頻繁建立執行緒的情況,我們都使用執行緒池來管理,所以我能想到的就是Dispathcer類內部維護了執行緒池,管理請求任務佇列,排程執行請求任務。那麼是不是這樣的呢?我們從Dispathcer原始碼中結合UML圖來找答案。
Dispathcer成員變數
//最大併發請求數為64
private int maxRequests = 64;
//每個主機最大請求數為5
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<>();
執行緒池的建立:
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;
}
同步請求:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
同步請求邏輯很簡單,就是將RealCall 加入到同步請求佇列。
非同步請求:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
非同步比同步複雜一些,判斷了佇列的長度是否大於maxRequests (64) 並且當前主機執行請求的數是否大於maxRequestsPerHost(5)。
true:
加入到正在執行的非同步請求佇列,呼叫執行緒池執行AsyncCall ,還記得AsyncCall 吧,它是RealCall的內部類,是一個執行緒類。
false:
加入到準備執行的非同步請求佇列中
因為執行緒池呼叫的是AsyncCall這個執行緒類,這裡我們就要跳到RealCall的內部類AsyncCall中去看相應的方法。我們知道AsyncCall既然是執行緒類一定有run方法,我們在講RealCall的時候,通過UML類圖可以發現AsyncCall繼承自NamedRunnable這個抽象類,NamedRunnable的run方法中執行了execute()方法,由於execute()方法是一個抽象方法,那麼轉了一圈我們又回到了AsyncCall類中來看具體實現。
@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);
}
}
同樣的看try{}部分
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
}
getResponseWithInterceptorChain()這個重要的方法也出現在了非同步中
finally部分
finally {
client.dispatcher().finished(this);
}
可以發現同步和非同步其實finally 部分都涉及到了Dispatcher類,都是呼叫了它的finished方法
其他重要方法
//非同步請求最終呼叫
/** 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!");
//根據傳入的promoteCalls判斷是否執行promoteCalls()方法,
//同步false非同步true
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
private void promoteCalls() {
//已經達到最大併發數直接return
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
//沒有準備執行的任務直接return
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.
}
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
無論是同步還是非同步請求,請求完成後呼叫了finished從佇列中移除call請求任務。
到這裡我們就可以對Dispatcher進行一個分析總結了,Dispatcher就像它的英文翻譯一樣排程器,無論是同步還是非同步都是通過RealCall呼叫Dispatcher這個okhttp的分發排程器通過執行緒池來執行具體的請求任務。請求完成後又將任務從佇列中清除。
Dispatcher總結:
傳送的同步和非同步請求都會在Dispatcher中進行狀態的管理,維護請求狀態,並且內部維護一個執行緒池用於執行請求。
到此我們的這一篇原始碼分析就基本完事了,到現在為止我們只是分析了一部分的執行過程,真正的網路請求部分我還沒有涉及到,而這些都是在同步和非同步請求中都出現的程式碼
Response response = getResponseWithInterceptorChain();
中體現,我們將在下一篇中進行分析,來講一講okhttp的攔截器。敬請期待