OkHttpソースコード分析(一)要求と応答過程の簡単な分析
この記事では、OkHttpのリクエストと応答プロセスについて簡単に説明します.
記事はOkHttp 3.14.3バージョンに基づいています
0はじめに
OkHttpは現在のJava系プログラミングのネットワークリクエストライブラリとして人気があるのは言うまでもない.ネット上のOkHttpの使用、パッケージ、ソース分析に関する文章とコードもすでに百家がそろっており、腐った街と言える.それでも、筆者はOkHttpの勉強や研究を記録して自分の内容を形成し、後で見やすいようにして書き始めましょう.
それは一番簡単なことから始まります.この論文ではOkHttpにおける大まかな要求応答過程を簡単に説明するつもりである.
1開始
まず、単純な同期GETリクエストと非同期GETリクエストを見てみましょう.同期GET要求: 非同期GET要求:
同期要求でも非同期要求でもStep 1~Step 3の3つのステップで要求タスクを構築し、call.execute()/call.enqueue(callback)を呼び出すことで同期/非同期要求を実行する必要があることがわかる.では、この2つの方法の実行過程を見てみましょう.
まずcall.execute()を見てみましょう.
Callクラスを表示するには、Callがインタフェースであることを確認し、call.execute()メソッドの特定のインプリメンテーションにジャンプしようとします(ps:ASショートカットCtrl+Alt+Bはメソッドの特定のインプリメンテーションに迅速にジャンプします)、Callインタフェースの唯一のインプリメンテーションクラスRealCallクラスに来て、RealCall.execute()は次のようにインプリメンテーションされます. Step 1.要求を実行する前に、call要求タスクがまだ実行されていないことを確認します.これにより、OkHttpは、要求されたタスクごとに1回しか実行できないことを要求していることがわかる. Step 2.タイムアウト時間の計算を開始し、要求を記録してcallStartというプロセスを開始します. Step 3.Dispatcherを使用して、同期要求キューrunningSyncCallsにこの要求タスクを記録します.runningSyncCallsはArrayDequeとして実装された双方向キューです: Step 4.次にRealCall.getResponseWithInterceptorChain()を呼び出して要求を開始し、要求結果を取得する. Step 5.結果が上位層に呼び出される前に、DispatcherによってrunningSyncCallsキューからこのタスクを削除し、このリクエストタスクが終了することを示す: . Step 6.要求結果を上位に戻し、要求を終了する.
以上のいくつかのステップを経て,一度の同期GET要求は終了する.RealCall.getResponseWithInterceptorChain()メソッドは、具体的なHTTPリクエストを担当していることがわかります.ここではしばらくフォローしません.まず、非同期GETリクエストを見てみましょう. Step 1.同期GET要求のStep 1と同じ. Step 2.要求プロセスを記録します. Step 3.AsyncCallオブジェクトを作成し、リクエストキューに追加します.AsyncCallはRealCallの内部クラスであり、非同期のリクエストタスクを表すために使用されます.AsyncCallの親であるNamedRunnableは、Runnableインタフェースを実装する抽象クラスであり、Runnable.runメソッドを書き換え、execute()抽象メソッドを外部に提供し、runメソッド実装で呼び出す.そこで、次に、AsyncCallクラス実装のexecute()メソッドに注目する.AsyncCallはRunnableであり、runではexecute()が呼び出されている以上、非同期要求タスクは最終的にここまで実行され、実際のHTTP要求: が実行されると判定することができる.
executeの実装を調べると、この判定が正しいことも検証され、いつ、どこから、このexecuteメソッドが実行されるかは、まず小さな穴を残してから記入しましょう.同時に、RealCall.execute()メソッド(同期要求)とAsyncCall.execute()メソッド(非同期要求)を比較すると、同期または非同期要求にかかわらず、最後にgetResponseWithInterceptorChain()メソッドを呼び出すことによって、ネットワーク要求が求められ、返された結果が得られることがわかります.このメソッドの実装を見てみましょう.
この方法は最終的にchain.proceed(originalRequest)によって行われることがわかる.リクエストとリターン結果を実現し,ここでOkHttpのリクエストプロセスは終了する.したがって、OkHttpの要求プロセスは、概ね次のようになる.
まずここまでにしましょう.一編書きたいと思っていたのに、長すぎて自分でも見たくないようなので、一歩一歩進んでみましょう.次編では,以上のOkHttpリクエストの過程で触れた各キークラスの解析に重点を置く.
2 The End :)
公衆番号へようこそ:
記事は個人ブログに初登場https://www.nullobject.cn、公衆番号
NullObject同期更新.
記事はOkHttp 3.14.3バージョンに基づいています
0はじめに
OkHttpは現在のJava系プログラミングのネットワークリクエストライブラリとして人気があるのは言うまでもない.ネット上のOkHttpの使用、パッケージ、ソース分析に関する文章とコードもすでに百家がそろっており、腐った街と言える.それでも、筆者はOkHttpの勉強や研究を記録して自分の内容を形成し、後で見やすいようにして書き始めましょう.
それは一番簡単なことから始まります.この論文ではOkHttpにおける大まかな要求応答過程を簡単に説明するつもりである.
1開始
まず、単純な同期GETリクエストと非同期GETリクエストを見てみましょう.
public static void getSync() {
// Step 1. HttpClient
OkHttpClient httpClient = new OkHttpClient();
// Step 2. Request 、 、
Request request = new Request.Builder().get()
.url("https://www.baidu.com")
.build();
// Step 3. Call
Call call = httpClient.newCall(request);
try {
// Step 4.
Response response = call.execute();
// Step 5. 、
ResponseBody responseBody = response.body();
if (responseBody != null) {
System.out.println(responseBody.string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void getAsync() {
// Step 1. HttpClient
OkHttpClient httpClient = new OkHttpClient();
// Step 2. Request 、 、
Request request = new Request.Builder().get()
.url("https://www.baidu.com")
.build();
// Step 3. Call
Call call = httpClient.newCall(request);
// Step 4.
call.enqueue(new Callback() {
@Override
public void onFailure(final Call call, final IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(final Call call, final Response response) throws IOException {
// Step 5. 、
ResponseBody responseBody = response.body();
if (responseBody != null) {
System.out.println(responseBody.string());
}
}
});
}
同期要求でも非同期要求でもStep 1~Step 3の3つのステップで要求タスクを構築し、call.execute()/call.enqueue(callback)を呼び出すことで同期/非同期要求を実行する必要があることがわかる.では、この2つの方法の実行過程を見てみましょう.
まずcall.execute()を見てみましょう.
Callクラスを表示するには、Callがインタフェースであることを確認し、call.execute()メソッドの特定のインプリメンテーションにジャンプしようとします(ps:ASショートカットCtrl+Alt+Bはメソッドの特定のインプリメンテーションに迅速にジャンプします)、Callインタフェースの唯一のインプリメンテーションクラスRealCallクラスに来て、RealCall.execute()は次のようにインプリメンテーションされます.
@Override public Response execute() throws IOException {
// Step 1.
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// Step 2.
transmitter.timeoutEnter();
transmitter.callStart();
try {
// Step 3.
client.dispatcher().executed(this);
// Step 4.
return getResponseWithInterceptorChain();
} finally {
// Step 5.
client.dispatcher().finished(this);
}
}
/** , . */
private final Deque runningSyncCalls = new ArrayDeque<>();
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
以上のいくつかのステップを経て,一度の同期GET要求は終了する.RealCall.getResponseWithInterceptorChain()メソッドは、具体的なHTTPリクエストを担当していることがわかります.ここではしばらくフォローしません.まず、非同期GETリクエストを見てみましょう.
Ctrl+Alt+B
call.enqueue(callback)の特定のインプリメンテーションRealCall.enqueue(callback):@Override public void enqueue(Callback responseCallback) {
// Step 1.
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// Step 2.
transmitter.callStart();
// Step 3.
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
@Override protected void execute() {
boolean signalledCallback = false;
// Step 3.1
transmitter.timeoutEnter();
try {
// Step 3.2
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
// Step 3.3 ,
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 {
//
responseCallback.onFailure(RealCall.this, e);
}
} finally {
// Step 4. ,
client.dispatcher().finished(this);
}
}
}
executeの実装を調べると、この判定が正しいことも検証され、いつ、どこから、このexecuteメソッドが実行されるかは、まず小さな穴を残してから記入しましょう.同時に、RealCall.execute()メソッド(同期要求)とAsyncCall.execute()メソッド(非同期要求)を比較すると、同期または非同期要求にかかわらず、最後にgetResponseWithInterceptorChain()メソッドを呼び出すことによって、ネットワーク要求が求められ、返された結果が得られることがわかります.このメソッドの実装を見てみましょう.
Response getResponseWithInterceptorChain() throws IOException {
//
List interceptors = new ArrayList<>();
//
interceptors.addAll(client.interceptors());
//
interceptors.add(new RetryAndFollowUpInterceptor(client));
// , ,
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//
interceptors.add(new CacheInterceptor(client.internalCache()));
//
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
// websocket , websocket ,
interceptors.addAll(client.networkInterceptors());
}
// ,
interceptors.add(new CallServerInterceptor(forWebSocket));
// chain
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
//
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
この方法は最終的にchain.proceed(originalRequest)によって行われることがわかる.リクエストとリターン結果を実現し,ここでOkHttpのリクエストプロセスは終了する.したがって、OkHttpの要求プロセスは、概ね次のようになる.
まずここまでにしましょう.一編書きたいと思っていたのに、長すぎて自分でも見たくないようなので、一歩一歩進んでみましょう.次編では,以上のOkHttpリクエストの過程で触れた各キークラスの解析に重点を置く.
2 The End :)
公衆番号へようこそ:
記事は個人ブログに初登場https://www.nullobject.cn、公衆番号
NullObject同期更新.