OKHttp
是目前 Android
平臺主流的網(wǎng)絡(luò)請求的基礎(chǔ)框架拥褂。因此我們有必要對其源碼進行閱讀學習紊册,了解其內(nèi)部的原理放前、項目結(jié)構(gòu)、以及請求的執(zhí)行過程痊焊。
它的項目地址為:https://github.com/square/okhttp
0x00 簡單使用
先從一個簡單的官方示例來看盏袄,這是一個同步 GET
請求
public class GetExample {
//1.http客戶端
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
//2.構(gòu)造請求
Request request = new Request.Builder()
.url(url)
.build();
//3.執(zhí)行請求,獲取響應(yīng)數(shù)據(jù)
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
public static void main(String[] args) throws IOException {
GetExample example = new GetExample();
String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
System.out.println(response);
}
}
可以看出這個 GET
請求操作是很簡單的薄啥。有幾個很重要的接口
-
OKHttpClient
: 它代表著http
客戶端 -
Request
:它封裝了請求對象辕羽,可以構(gòu)造一個http
請求對象 -
Response
:封裝了響應(yīng)結(jié)果 -
Call
:client.newCall
調(diào)用后生成一個請求執(zhí)行對象Call
,它封裝了請求執(zhí)行過程垄惧。
這幾個接口是程序員在使用 OKHttp
庫中經(jīng)常遇到的刁愿。
接下來將從這個示例開始閱讀 OkHttp
的源碼
0x01 Call.execute()
跟進源碼后發(fā)現(xiàn)這個方法是在 Call
中的接口
/**
* A call is a request that has been prepared for execution. A call can be canceled. As this object
* represents a single request/response pair (stream), it cannot be executed twice.
*/
public interface Call extends Cloneable {
//...
//同步執(zhí)行請求
Response execute() throws IOException;
//將請求加入隊列
void enqueue(Callback responseCallback);
//...
}
從源碼注釋知道,Call
是一個準備請求的執(zhí)行對象赘艳,它可以被取消,代表一個 “請求/響應(yīng)” 對克握,不能執(zhí)行兩次蕾管。
RealCall
Call
的實現(xiàn)類是 RealCall
,因此 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);
}
}
這個方法也不是很長菩暗,邏輯很簡單:
- 同步鎖檢查該請求是否已經(jīng)執(zhí)行掰曾,如果沒有則標記
executed = ture
,否則拋出異常 - 調(diào)用了回調(diào)函數(shù)
callStart
-
okhttp
客戶端調(diào)用dispatcher
將執(zhí)行請求對象 - 調(diào)用了
getResponseWithInterceptorChain
方法獲取到響應(yīng)數(shù)據(jù)Response
停团,這個方法很重要旷坦,后面會繼續(xù)跟進 - 然后是對請求失敗的回調(diào)
callFailed
- 最后還是使用
dispather
對象調(diào)用finished
方法掏熬,完成請求
這里的邏輯還是比較清晰的,出現(xiàn)兩個重要的方法
dispatcher.execute
getResponseWithInterceptorChain
接下來分別看這兩個方法
0x02 Dispatcher
public final class Dispatcher {
/** 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<>();
//...
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
/** 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!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
}
可以看出Dispatcher
是一個調(diào)度器秒梅,它內(nèi)部有一個線程池executorService
,還有三個隊列旗芬,分別代表同步請求進行隊列、異步請求等待隊列捆蜀、異步請求執(zhí)行隊列疮丛。
我們發(fā)現(xiàn)調(diào)用execute
方法時就是將Call
對象加入到同步請求進行隊列runningSyncCalls
中,而調(diào)用finished
方法則是將Call
請求從隊列中移除
0x03 getResponseWithInterceptorChain
現(xiàn)在在回到RealCall
源碼中辆它,這個方法可以說是OkHttp
最關(guān)鍵的部分了
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()));//處理cookie的攔截器
interceptors.add(new CacheInterceptor(client.internalCache()));//處理緩存的攔截器
interceptors.add(new ConnectInterceptor(client));//負責連接的攔截器
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());//添加程序員自定義的network攔截器
}
interceptors.add(new CallServerInterceptor(forWebSocket));//調(diào)用服務(wù)攔截器
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
在添加了一系列的攔截器之后誊薄,又構(gòu)造了一個攔截器責任鏈,這個RealInterceptorChain
包含了所有的攔截器對象锰茉。然后調(diào)用chain.proceed
方法開始執(zhí)行請求呢蔫,這時就到了RealInterceptorChain
這個類中。
0x04 RealInterceptorChain
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
//省略無關(guān)代碼...
//1. 執(zhí)行攔截器責任鏈中的下一個攔截器
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//2. 獲取當前的攔截器
Interceptor interceptor = interceptors.get(index);
//3. 執(zhí)行攔截飒筑,并返回響應(yīng)
Response response = interceptor.intercept(next);
//省略...
return response;
}
可以看到片吊,在proceed
方法,又構(gòu)造了RealInterceptorChain
并且調(diào)用了interceptor.intercept
方法扬霜,
而這個方法中又會調(diào)用next.proceed
方法定鸟,直至返回response
。這個過程有點像遞歸調(diào)用著瓶。
0x05 Interceptor
攔截器联予,它是一個接口,內(nèi)部還有一個Chain
接口
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
所有的攔截器都需要實現(xiàn)這個接口材原。
0x06 異步的情況
public final class AsynchronousGet {
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
//調(diào)用enqueue方法沸久,并設(shè)置回調(diào)接口
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override public void onResponse(Call call, Response response) throws IOException {
//這里獲取到響應(yīng)結(jié)果數(shù)據(jù)
}
});
}
然后我們再看RealCall
中的enqueue
方法
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//最終執(zhí)行了dispatcher的enqueue方法
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
其實是執(zhí)行了dispatcher
中的enqueue
方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
在dispatcher
中通過線程池來執(zhí)行AsyncCall
對象,因此跟進到AsyncCall
中的execute
方法
@Override protected void execute() {
boolean signalledCallback = false;
try {
//最終還是調(diào)用了getResponseWithInterceptorChain()S嘈贰>砜琛!
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);
}
}
發(fā)現(xiàn)最終還是執(zhí)行了getResponseWithInterceptorChain
威酒,因此不管是同步還是異步窑睁、最終的流程還是一樣。
0x07 總結(jié)
OKHttpClient
這是一個 http
客戶端葵孤。構(gòu)建很簡單担钮,可以使用無參構(gòu)造函數(shù)。其內(nèi)部是通過 Builder
對象進行構(gòu)建的尤仍。也可以通過其內(nèi)部靜態(tài)類 Builder
來構(gòu)建箫津,然后通過 builder
設(shè)置 OkHttpClient
構(gòu)造參數(shù)。
Request
請求對象。其內(nèi)部也是使用 Builder
模式封裝了構(gòu)造的過程苏遥,通過Builder
使用鏈式調(diào)用也是目前很多開源庫中常見的模式饼拍。
Response
響應(yīng)結(jié)果√锾浚客戶端執(zhí)行后返回響應(yīng)結(jié)果师抄,通過 Response
可以很方便的獲取到響應(yīng)數(shù)據(jù)。
Call
請求執(zhí)行诫肠∷九欤可以執(zhí)行同步或者異步的請求,分別將請求發(fā)送到dispatcher
Dispatcher
調(diào)度器栋豫。其內(nèi)部有一個線程池挤安,并維護了三個隊列:同步進行請求隊列、異步請求等待隊列丧鸯、異步請求進行隊列蛤铜。
還有兩個重要的方法execute
和enqueue
方法,分別代表同步丛肢、異步的方法围肥。這兩個方法的最終的執(zhí)行流程都是一樣的
Interceptor
攔截器。攔截器在OKHttpClient
中使是用責任鏈模式來實現(xiàn)的蜂怎。Okhttp
中的關(guān)鍵的流程是通過攔截器責任鏈來完成的穆刻。