關於 OkHttp
OkHttp 是一個適用於 Android 和 Java 應用程式的 HTTP+HTTP/2客戶端。
如何使用
Android 開發只需新增依賴,如下:
implementation("com.squareup.okhttp3:okhttp:3.13.1")
官方示例1:獲取一個 url 上的內容並輸出
//Http 客戶端
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
//構造請求
Request request = new Request.Builder()
.url(url)
.build();
//執行請求,獲取資料
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
複製程式碼
官方示例2:給伺服器post
資料
public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
複製程式碼
簡單分析一下這段程式碼:這個post
請求操作看起來很簡單。但是我們需要學習其中幾個很重要的介面:
OKHttpClient:它代表著 http 客戶端
Request:它封裝了請求物件,可以構造一個 http 請求物件
Response:封裝了響應結果
Call:client.newCall()呼叫後生成一個請求執行物件Call,它封裝了請求執行過程。
下面我們結合這個例子來分析原始碼:
newCall().execute()
跟蹤原始碼後發現這個方法是在 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 {
//省略部分程式碼
//同步執行請求
Response execute() throws IOException;
//請求加入佇列
void enqueue(Callback responseCallback);
//省略部分程式碼
}
複製程式碼
Call 的實現類是 RealCall,繼續追蹤原始碼的 RealCall.java 檔案,可以看到 execute 方法:
@Override public Response execute() throws IOException {
//同步鎖檢查該請求是否已經執行,如果沒有則標記executed = ture,否則丟擲異常
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
//呼叫了回撥方法 callStart
eventListener.callStart(this);
try {
//okhttp 客戶端呼叫 dispatcher 將執行請求物件
client.dispatcher().executed(this);
//呼叫了 getResponseWithInterceptorChain 方法獲取到響應資料 Response,後期還會繼續分析
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
//請求失敗的回撥 callFailed
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
//使用 dispather 物件呼叫 finished 方法,完成請求
}
}
複製程式碼
接下來我們詳細分析一下 dispatcher.execute 和 getResponseWithInterceptorChain 這兩個方法:
跟蹤原始碼 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<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
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;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
}
複製程式碼
發現 Dispatcher 是一個排程器,它的作用是對請求進行分發。它的內部有三個佇列,分別是同步請求進行佇列、非同步請求等待佇列、非同步請求執行佇列。
核心方法:getResponseWithInterceptorChain
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()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
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);
}
複製程式碼
此方法構建數個攔截器之後,又構造了一個攔截器責任鏈。跟蹤原始碼 RealInterceptorChain:
RealInterceptorChain
該類主要負責將所有的攔截器串連起來,使所有的攔截器以遞迴的方式進行實現,從而確保只有所有的攔截器都執行完之後才會返回 Response。以下對該類中的主要方法 proceed 進行講解:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
//用於計算攔截器呼叫該方法的次數
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
//生成下一個攔截器調動該方法的 RealInterceptorChain 物件,其中 index+1 用於獲取下一個攔截器
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
//獲取當前攔截器
Interceptor interceptor = interceptors.get(index);
//呼叫該攔截器並獲取返回結果
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
複製程式碼
總結
以上就是我們對 OkHttp 核心原始碼的分析。當我們發起一個請求的時候會初始化一個 Call 的例項,然後根據同步和非同步的不同,分別呼叫它的 execute() 和 enqueue() 方法。大致過程都是通過攔截器組成的責任鏈,依次經過重試、橋接、快取、連線和訪問伺服器等過程,來獲取到一個響應並交給使用者。