一、概述
在日常開發(fā)中最常用的請求框架就是Okhttp了,本文將對okhttp的請求流程 由淺入深進(jìn)行分析,由于我項目中使用的Okhttp版本為3.12.0,所以我們根據(jù)3.12.0的源碼進(jìn)行分析。
Okhttp特點:
OkHttp 是一個默認(rèn)高效的 HTTP 客戶端:
- HTTP/2 支持允許對同一主機的所有請求共享一個套接字。
- 連接池減少了請求延遲(如果 HTTP/2 不可用)。
- 透明 GZIP 縮小了下載大小。
- 響應(yīng)緩存完全避免了網(wǎng)絡(luò)重復(fù)請求。
二、源碼分析
在源碼分析之前,我們先看一下Okhttp的簡單使用。
//創(chuàng)建一個OkhttpClient
OkHttpClient client = new OkHttpClient();
//創(chuàng)建Request
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
//同步請求
Response response = client.newCall(request).execute();
這樣就很簡單的實現(xiàn)了一個請求,我們一點一點來分析,先來看OkhttpClient
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
//省略部分代碼
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
//省略部分代碼
}
可以看到在創(chuàng)建OkHttpClient時,使用的是建造者模式,最終創(chuàng)建出OkhttpClient,Builder的代碼太長這里就不貼了,就是對一些配置的初始化,例如超時時間等,另外我們可以通過這個Builder.build()方法來構(gòu)建出OkhttpClient。
接下來我們再來看Request是什么:
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;
private volatile @Nullable CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
//省略部分代碼
}
Request包含的就是請求的url,method,header,RequestBody等信息。同樣是通過Builder來構(gòu)建。
最后就是執(zhí)行請求了,到這里之后就是重點了,好好看哦,
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
同樣我們按照順序,先來看client的newCall方法做了什么:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
newCall方法返回值為Call,返回了一個RealCall(Call的實現(xiàn)類)。
既然newCall返回的是RealCall,那么enqueue方法就一定再RealCall里面,就是說我們的同步/異步請求最終都是通過這個RealCall來執(zhí)行的。所以我們來看RealCall的enqueue()方法:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
//如果為true 直接拋出異常,說明這個請求已經(jīng)執(zhí)行了
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
//事件監(jiān)聽回調(diào)
eventListener.callStart(this);
//重點
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
上面的最后一行代碼又調(diào)用了client.dispatcher().enqueue()方法,就是Dispatcher類的enqueue(),我們來點進(jìn)去看:
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
enqueue方法又將call加入了readyAsyncCalls,我們先來看看這是啥:
/** 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<>();
在Dispatcher類中有三個雙端隊列:
readyAsyncCalls:準(zhǔn)備隊列,
runningAsyncCalls:運行時異步隊列
runningSyncCalls:運行時同步隊列
所以說上面的enqueue方法中,就是將call先放到了準(zhǔn)備隊列,然后調(diào)用了promoteAndExecute()方法,我們來看看這個方法:
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
//用于保存要執(zhí)行請求的隊列
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//如果運行中隊列大于maxRequests 64 終止執(zhí)行
//就是正在請求的數(shù)量 不能大于64
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
//如果相同host同時請求大于maxRequestsPerHost 結(jié)束本次循環(huán)
//就是對同一個主機的同時請求 不能大于5
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
//符合條件時,將請求從準(zhǔn)備隊列中移除
i.remove();
//將請求加到執(zhí)行隊列
executableCalls.add(asyncCall);
//將請求加到運行中隊列
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
//遍歷執(zhí)行隊列
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
//執(zhí)行請求
asyncCall.executeOn(executorService());
}
return isRunning;
}
promoteAndExecute()方法的主要操作就是將符合執(zhí)行條件的請求執(zhí)行,最終調(diào)用的是asyncCall.executeOn(executorService())方法,下面我們先來看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;
}
這個方法其實就是返回了一個線程池,所以說異步請求是通過線程池來處理的,我們再返回去看asyncCall.executeOn()方法:
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
//通過線程池 執(zhí)行請求 this = RealCall
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
//異常處理
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
//
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
在上面的方法中,調(diào)用了線程池的execute(this)方法,就相當(dāng)于調(diào)用了RealCall的execute,繼續(xù)來看這個方法:
@Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
//這里最終通過攔截器 來拿到response
Response response = getResponseWithInterceptorChain();
//下面就是一些判斷處理操作 就不詳細(xì)看了
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
//回調(diào)
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
e = timeoutExit(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 {
//最終調(diào)用了finished
client.dispatcher().finished(this);
}
}
RealCall的execut方法就是通過攔截器來拿到response,最終調(diào)用了client.dispatcher().finished(this)方法,攔截器我們先放在后面講,先繼續(xù)看client.dispatcher().finished(this):
//最終調(diào)用的這個方法
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
//這里是重點 這里又調(diào)用了promoteAndExecute()
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
到這里是不是有一點豁然開朗了?這個方法里又調(diào)用了promoteAndExecute()方法,還記得剛開始的enqueue里面也是執(zhí)行了這個方法嘛?所以說當(dāng)一個請求結(jié)束后,又再次處理隊列中的其他請求。
到這里整個請求的流程就結(jié)束了。我們繼續(xù)來看一下上面提到的攔截器;
Response response = getResponseWithInterceptorChain();
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//聲明一個list 存儲攔截器
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));
//自定義的的network攔截器
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
//請求服務(wù)攔截器
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);
}
在okhttp中有五種攔截器:重試攔截器,橋接攔截器,緩存攔截器,連接攔截器,請求服務(wù)攔截器,另外還有用戶自定義的攔截器。其中攔截器是責(zé)任鏈模式,通過層層調(diào)用最終返回我們想要的Response,攔截器暫時不深入介紹了,后面有時間再研究吧。
異步請求看完之后,接下來就剩同步請求了,其實同步請求更簡單,
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
同步請求則直接將請求通過dispatcher加入了運行中同步隊列中,其他處理與異步請求大致相同,這里就不做太多介紹了。
三、總結(jié)
至此我們已經(jīng)分析完了Okhttp請求的處理流程,我們最后總結(jié)一下:
- 創(chuàng)建OkhttpClient
- 構(gòu)建Request請求
- newCall得到RealCall
- 通過RealCall調(diào)用enqueue/execute
- 通過dispatcher進(jìn)行任務(wù)分發(fā)
- 最終通過攔截器得到Response
最后畫一張流程圖僅供參考:
