前回の記事では、Retrofit でのアノテーション解析と動的プロキシ実装について詳しく分析しましたが、この記事では、引き続き Retrofit がネットワーク リクエストとレスポンスを処理する方法に焦点を当てて、Retrofit のコア ソース コードを詳しく調査していきます。
ネットワークリクエスト
Retrofit を使用してネットワーク リクエストを開始する場合、インターフェイスを定義し、Retrofit アノテーションを使用してこのインターフェイス内のリクエストを記述することができます。Retrofit は、インターフェイスを実装するプロキシ オブジェクトを自動的に生成します。このプロキシ オブジェクトのメソッドを呼び出すと、Retrofit はアノテーションの記述に従って Request オブジェクトを構築し、OkHttp を使用してリクエストを送信します。
Retrofit ではRetrofit#execute
、Retrofit#enqueue
または メソッドを通じてリクエストを送信できます。これら 2 つのメソッドの違いは、execute
メソッドはリクエストが完了するまで現在のスレッドをブロックするのに対し、enqueue
メソッドはリクエストを OkHttp リクエスト キューに追加し、リクエストが完了するとコールバックを通じて通知することです。
まずexecute
メソッド。
public <T> T execute(Call<T> call) throws IOException {
Utils.validateServiceInterface(call.request().tag(), call.request().url().toString());
return (T) callAdapter(call, call.request().tag()).adapt(call).execute();
}
この方法では、最初にインターフェイスが検証されて、インターフェイスが有効であることが確認されます。次に、リクエストの Tag と URL に基づいてアダプタを取得しcallAdapter
、そのアダプタを使用してリクエストを実行します。
アダプターの役割は、リクエストのパラメーターを OkHttp が認識できる形式に適応させ、OkHttp の応答を必要な形式に適応させることです。Retrofit は、Call アダプター、RxJava アダプター、CompletableFuture アダプターなどの一連のアダプターを提供します。
callAdapter
メソッドの実装を見てみましょう。
private CallAdapter<?, ?> callAdapter(Call<?> call, Object tag) {
Type responseType = call.request().method().equals("HEAD")
? Void.class
: getParameterUpperBound(0, (ParameterizedType) call.request().tag());
return callAdapter(tag, responseType);
}
このメソッドでは、まずリクエストメソッドに応じてレスポンスタイプを判定し、HEADメソッドであればレスポンスタイプはVoid、そうでない場合はリフレクションによりリクエストのレスポンスタイプを取得し、そのレスポンスタイプを利用してレスポンスタイプを取得します。アダプターを入手します。
アダプターを入手する方法は次のとおりですcallAdapter
。
public <R, T> CallAdapter<R, T> callAdapter(Object tag, Type returnType) {
// ...
for (CallAdapter.Factory factory : adapterFactories) {
CallAdapter<?, ?> adapter = factory.get(returnType, annotations, this);
if (adapter != null) {
return (CallAdapter<R, T>) adapter;
}
}
// ...
}
このメソッドでは、すべてのアダプター ファクトリを反復処理して、アダプターを取得しようとします。アダプターを取得するときに、リクエストの応答タイプ、アノテーション、および Retrofit インスタンスをパラメーターとして渡します。各アダプター ファクトリは、これらのパラメーターが独自の適応条件を満たしているかどうかを判断し、満たしている場合はアダプター インスタンスを返し、そうでない場合は null を返します。すべてのアダプター ファクトリを走査した後、アダプターが取得されなかった場合は、例外がスローされます。
アダプターを取得したら、アダプターを使用してリクエストを実行できます。アダプターでは、リクエスト パラメーターを OkHttp Request オブジェクトに変換し、OkHttp Response オブジェクトを必要な応答タイプに変換します。具体的な実装については、 Retrofit が提供するCallAdapter
インターフェース。
enqueue
メソッドについては、まずenqueue
メソッドの実装を確認します。
public <T> void enqueue(Call<T> call, Callback<T> callback) {
Utils.validateServiceInterface(call.request().tag(), call.request().url().toString());
callAdapter(call, call.request().tag()).adapt(call).enqueue(new CallbackRunnable<>(callback));
}
このメソッドでは、最初にインターフェイスを確認し、次に要求されたタグと URL に従ってアダプターを取得し、そのアダプターを使用して要求を実行します。違いは、enqueue
メソッドCallback オブジェクトをパラメータとしてenqueue
アダプタのメソッドに渡し、リクエストの完了後にコールバックが通知するようにすることです。
アダプターでは、enqueue
メソッド。
public void enqueue(final Callback<T> callback) {
delegate.enqueue(new Callback<Response<T>>() {
@Override public void onResponse(Call<Response<T>> call, Response<Response<T>> response) {
Response<T> body;
try {
body = response.body();
} catch (Throwable t) {
if (response.code() == 204) {
body = null;
} else {
callback.onFailure(call, t);
return;
}
}
if (response.isSuccessful()) {
callback.onResponse(call, Response.success(body, response.raw()));
} else {
callback.onFailure(call, Response.error(response.errorBody(), response.raw()));
}
}
@Override public void onFailure(Call<Response<T>> call, Throwable t) {
callback.onFailure(call, t);
}
});
}
このメソッドでは、受信した Callback オブジェクトをCallback<Response<T>>
object、このオブジェクトを使用して OkHttp の enqueue メソッドを呼び出します。リクエスト完了後、OkHttpのResponseオブジェクトをRetrofitのResponseオブジェクトに変換し、レスポンスコードに応じてリクエストの結果を判定します。応答コードがリクエストが成功したことを示している場合は、 Callback オブジェクトのonResponse
メソッドを。それ以外の場合は、 onFailure
Callback オブジェクトのメソッドを呼び出します。
応答処理
Retrofit では、インターフェイスを定義し、アノテーションを使用することで、目的のリクエスト形式と応答形式を記述することができます。たとえば、@GET
アノテーションて GET リクエストを記述したり、@Query
アノテーションを使用してリクエスト パラメータを記述したり、@Body
アノテーションてリクエスト本文を記述@Headers
したり、アノテーションを使用してリクエスト ヘッダーを記述したりすることができます。
リクエストを実行すると、Retrofit はこれらのアノテーションに基づいて対応するリクエスト オブジェクトを自動的に生成し、リクエスト オブジェクトを OkHttp Request オブジェクトに変換します。応答を受信すると、Retrofit は OkHttp Response オブジェクトを対応する応答オブジェクトに変換し、応答オブジェクト内のデータを必要なデータ型に変換します。これらの変換は、Retrofit のコンバータを通じて行われます。Retrofit には、デフォルトで と の 2 つのコンバータが用意されていGsonConverterFactory
ますJacksonConverterFactory
。コンバータをカスタマイズして、目的のデータ変換を実現することもできます。
Retrofit クラスの構築メソッドでは、Retrofit がデフォルトでPlatform.get()
メソッドconverterFactories
。その後、addConverterFactory
このメソッド。
public Retrofit(Builder builder) {
// ...
if (builder.converterFactories == null) {
converterFactories.add(Platform.get().defaultConverterFactory());
} else {
converterFactories.addAll(builder.converterFactories);
}
// ...
}
public interface Platform {
// ...
Converter.Factory defaultConverterFactory();
}
このメソッドではexecute
、アダプターのadapt メソッドを呼び出してリクエストを実行し、返された Call オブジェクトを応答オブジェクトに変換します。変換プロセス中に、変換の応答タイプに応じて、対応するコンバーターが選択されます。具体的な変換実装については、 Retrofit が提供するConverter
インターフェースおよびインターフェースを参照してください。Converter.Factory
public <T> T execute(Call<T> call) throws IOException {
// ...
Response<T> response = call.execute();
if (response.isSuccessful()) {
return response.body();
} else {
Converter<ResponseBody, ErrorResponse> converter = retrofit.responseBodyConverter(
ErrorResponse.class, new Annotation[0]);
throw new ApiException(converter.convert(response.errorBody()));
}
}
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public <T> T adapt(Call<T> call) {
return (T) new OkHttpCall<>(requestFactory, callFactory, converter, call);
}
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
Objects.requireNonNull(type, "type == null");
Objects.requireNonNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
return (Converter<ResponseBody, T>) converter;
}
}
throw new IllegalArgumentException(
"Could not locate ResponseBody converter for " + type + " with annotations " + Arrays.toString(annotations));
}
上記は、Retrofit で応答を処理するためのコア コードです。リクエストを実行すると、Retrofit はまずリクエストを OkHttp Request オブジェクトに変換して送信し、次に応答が返されるのを待ちます。応答が返されると、Retrofit は応答を応答オブジェクトに変換し、応答オブジェクト内のデータを期待するデータ型に変換します。このプロセスでは、Retrofit が提供するコンバーターを使用して、データ変換ルールをカスタマイズできます。
以下は、Retrofit を使用して GET リクエストを送信し、応答内の JSON データを Java オブジェクトに変換する方法の例です。
public interface ApiService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<Repo>> call = apiService.listRepos("smallmarker");
List<Repo> repos = call.execute().body();
上の例では、最初に Retrofit ビルダーを使用して Retrofit インスタンスを作成し、リクエストのベース URL とコンバータ ファクトリを指定しました。次にcreate
、ApiService
メソッドを呼び出してプロキシ オブジェクトを作成します。最後に、listRepos
メソッドをGET リクエストを送信します。
上の例では、Retrofit を使用してGsonConverterFactory
、応答本文の JSON データを Java オブジェクトに変換しました。具体的な実装については、 Retrofit が提供するGsonConverterFactory
クラス。
public final class GsonConverterFactory extends Converter.Factory {
private final Gson gson;
private GsonConverterFactory(Gson gson) {
this.gson = gson;
}
public static GsonConverterFactory create() {
return create(new Gson());
}
public static GsonConverterFactory create(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
return new GsonConverterFactory(gson);
}
@Override
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
@Override
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
}
GsonConverterFactory
RetrofitConverter.Factory
クラス、responseBodyConverter
メソッドとrequestBodyConverter
メソッドが書き換えられていることがわかります。responseBodyConverter
メソッドでは、レスポンス本文の JSON データを Java オブジェクトに変換し、requestBodyConverter
メソッド では、Java オブジェクトをリクエスト本文の JSON データに変換します。
GsonConverterFactory
Retrofit は、に加えて、JacksonConverterFactory、MoshiConverterFactory
などの他のコンバータも提供しています。ニーズに応じて適切なコンバータを選択できます。
一般に、Retrofit のネットワーク要求および応答処理のコア コードは非常に簡潔かつ明確です。インターフェースを定義してリクエストとレスポンスを記述し、Retrofit の動的プロキシ メカニズムを使用してインターフェースを実際の実装クラスに変換し、Retrofit 設定を通じてリクエストとレスポンスのコンバータを指定するだけです。この方法により、ネットワーク リクエストのプロセスが大幅に簡素化され、ビジネス ロジックの処理により集中できるようになります。