Java/Kotlin向けHTTPクライアントライブラリRetrofit 詳細ガイド

この記事から得られる知識

  • Retrofitの基本的な概念と、なぜ多くの開発者に選ばれるのかが分かります。
  • HTTPリクエストを定義するためのインターフェースとアノテーションの具体的な使い方を習得できます。
  • JSONレスポンスをJava/Kotlinオブジェクトに自動変換する方法(Converterの利用)を学べます。
  • リクエストの前処理や共通ヘッダの付与など、Interceptorを活用した応用的な使い方を理解できます。
  • Kotlin CoroutinesやRxJavaと連携し、効率的な非同期処理を実装する方法を把握できます。
  • API通信における実践的なエラーハンドリングの方法を学べます。

はじめに:Retrofitとは何か?

Retrofitは、AndroidおよびJava/Kotlinアプリケーション開発において、型安全なHTTPクライアントとして絶大な人気を誇るライブラリです。 Square社によって開発され、REST APIとの通信処理を驚くほどシンプルかつ宣言的に記述できるように設計されています。

通常、HTTP通信を実装するには、URLの構築、リクエストパラメータの設定、レスポンスのパース(解析)、エラーハンドリングといった煩雑な定型コード(ボイラープレートコード)を大量に書く必要がありました。 Retrofitは、これらの処理の多くをライブラリ内部で吸収し、開発者が本来集中すべきビジネスロジックの実装に専念できるようにしてくれます。

Retrofitの核心

Retrofitの最大の特徴は、HTTP APIをJavaやKotlinのインターフェースとして定義することです。 アノテーションを使ってHTTPメソッド(GET, POSTなど)やURL、パラメータを指定するだけで、Retrofitがそのインターフェースの実装を動的に生成してくれます。 これにより、まるでローカルのメソッドを呼び出すかのように、直感的にAPIを叩くことが可能になります。

準備編:プロジェクトにRetrofitを導入する

Retrofitを利用するためには、まずプロジェクトにいくつかのライブラリを追加する必要があります。 ここでは、ビルドツールとして一般的なGradleを使用する例を解説します。

1. 依存関係の追加 (build.gradle)

アプリケーションモジュールの `build.gradle` または `build.gradle.kts` ファイルに、以下の依存関係を追記します。

// build.gradle (Groovy)

// 最新のバージョンは公式リポジトリ等で確認してください
def retrofit_version = "2.9.0"
def okhttp_version = "4.9.3" // Retrofitは内部でOkHttpを利用しています

dependencies {
    // Retrofit本体
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"

    // JSONを扱うためのConverter (ここではMoshiを使用)
    implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
    // Moshi本体
    // implementation "com.squareup.moshi:moshi-kotlin:1.13.0" // Kotlinで利用する場合

    // (オプション) 通信ログを確認するためのInterceptor
    implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"
}

Converterとは?

APIから返されるデータ形式(多くはJSON)と、プログラムで扱うJava/Kotlinオブジェクトとの間の変換を担うのがConverterです。 RetrofitはConverterを差し替えることで、様々なデータ形式に対応できます。

代表的なJSON Converterには以下のようなものがあります。

  • Moshi: Kotlinとの親和性が高く、パフォーマンスも良好なため、近年人気があります。
  • Gson: Google製で、古くから広く使われている定番のライブラリです。
  • Jackson: 高機能で柔軟な設定が可能なライブラリです。

この記事では、Kotlinとの相性の良さからMoshiを主に使用して解説します。

2. インターネット接続権限の付与

Androidアプリでネットワーク通信を行うには、`AndroidManifest.xml`にインターネット接続のパーミッションを宣言する必要があります。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <!-- この行を追加 -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        ...>
        ...
    </application>
</manifest>

基本編:Retrofitの3ステップ

Retrofitを使った通信処理は、大きく分けて以下の3つのステップで構成されます。

  1. APIの定義 (インターフェースの作成)
  2. Retrofitインスタンスの生成
  3. API呼び出しと実行

Step 1. APIの定義 (インターフェースの作成)

まず、通信したいAPIのエンドポイントをインターフェースとして定義します。 ここでアノテーションが大活躍します。

例として、GitHubのユーザーリポジトリを取得するAPI (`https://api.github.com/users/{user}/repos`) を定義してみましょう。

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import java.util.List;

// レスポンスのJSONに対応するデータクラス
// 例: [{"id": 123, "name": "my-repo", ...}]
public class Repo {
    long id;
    String name;
    // ... 他のフィールド
}

// APIのインターフェース
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}
  • `@GET(“users/{user}/repos”)`: このメソッドがHTTPのGETリクエストであることを示します。 引数の文字列がエンドポイントのパスになります。
  • `{user}`: URL内にある動的な部分(パスパラメータ)をプレースホルダで示します。
  • `@Path(“user”) String user`: メソッドの引数 `user` の値を、`@GET`アノテーションで定義したプレースホルダ `{user}` に埋め込みます。
  • `Call<List<Repo>>`: メソッドの戻り値です。 `Call`はRetrofitにおけるAPI呼び出しを表す型で、ジェネリクスにはレスポンスを変換したい型(ここでは`Repo`オブジェクトのリスト)を指定します。

Step 2. Retrofitインスタンスの生成

次に、定義したインターフェースを実装するオブジェクトを生成するために、Retrofitのインスタンスを作成します。 このインスタンスはアプリケーション内で一つだけ作成し、使い回すのが一般的です。

import retrofit2.Retrofit;
import retrofit2.converter.moshi.MoshiConverterFactory;

public class ApiClient {

    private static final String BASE_URL = "https://api.github.com/";

    private static Retrofit retrofit = null;

    public static Retrofit getClient() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL) // ① ベースURLの設定
                    .addConverterFactory(MoshiConverterFactory.create()) // ② Converter-Factoryの追加
                    .build(); // ③ Retrofitインスタンスの生成
        }
        return retrofit;
    }
}
  • ① `.baseUrl()`: APIのベースとなるURLを指定します。 エンドポイントのパスはこのURLに連結されます。末尾は必ず `/` にする必要があります。
  • ② `.addConverterFactory()`: レスポンスをパースするためのConverter-Factoryを指定します。 ここではMoshiを使用しています。
  • ③ `.build()`: 設定に基づいてRetrofitインスタンスを生成します。

Step 3. API呼び出しと実行

最後に、作成したRetrofitインスタンスからサービスクラス(インターフェースの実装)を取得し、メソッドを呼び出します。

Retrofitの`Call`オブジェクトは、同期実行と非同期実行の両方の方法を提供します。

非同期実行 (`enqueue`)

Androidアプリなど、UIスレッドをブロックできない環境では非同期実行が必須です。 `enqueue`メソッドにコールバックを渡すことで、別スレッドで通信を行い、結果をUIスレッドで受け取ることができます。

GitHubService service = ApiClient.getClient().create(GitHubService.class);
Call<List<Repo>> call = service.listRepos("google");

call.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
        if (response.isSuccessful()) {
            // 通信成功時の処理 (HTTPステータスコード 200-299)
            List<Repo> repos = response.body();
            // 取得したデータを使ってUIを更新するなどの処理
        } else {
            // 通信成功だが、サーバーがエラーを返した場合の処理
            // (例: 404 Not Found, 500 Internal Server Error)
        }
    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
        // 通信失敗時の処理 (ネットワーク接続がない、タイムアウトなど)
    }
});

同期実行 (`execute`)

`execute`メソッドを呼び出すと、通信が完了するまで現在のスレッドがブロックされます。 そのため、Androidのメインスレッドで直接呼び出すことはできず、バックグラウンドスレッドで実行する必要があります。

// このコードはバックグラウンドスレッドで実行する必要がある
try {
    GitHubService service = ApiClient.getClient().create(GitHubService.class);
    Call<List<Repo>> call = service.listRepos("google");

    Response<List<Repo>> response = call.execute(); // 同期的にAPIを呼び出す
    if (response.isSuccessful()) {
        List<Repo> repos = response.body();
        // 成功時の処理
    } else {
        // エラー時の処理
    }
} catch (IOException e) {
    // 通信失敗時の処理
    e.printStackTrace();
}

実践編:アノテーションを使いこなす

Retrofitの真価は、その豊富なアノテーションにあります。様々なAPIの仕様に柔軟に対応するための主要なアノテーションを見ていきましょう。

1. HTTPメソッド

リクエストの種類を指定します。主要なものは以下の通りです。

アノテーション 説明
@GET リソースを取得します。
@POST リソースを新規作成します。リクエストボディを伴うことが多いです。
@PUT リソースを更新または作成します(全体を置換)。
@PATCH リソースを部分的に更新します。
@DELETE リソースを削除します。
@HEAD GETと似ていますが、レスポンスボディは返さずヘッダー情報のみを取得します。
@OPTIONS 対象リソースがサポートする通信オプション(HTTPメソッドなど)を問い合わせます。
@HTTP 上記以外、または動的にHTTPメソッドを指定したい場合に使用する汎用的なアノテーションです。

2. URL操作

URLを動的に構築するためのアノテーションです。

アノテーション 説明 使用例
@Path URLのパスの一部を置き換えます。URL内のプレースホルダ({key})と対応させます。 @GET("group/{id}/users")<br/>Call<...> groupList(@Path("id") int groupId);
@Query クエリパラメータを追加します。URLの末尾に ?key=value の形式で付与されます。 @GET("data/2.5/weather")<br/>Call<...> getWeather(@Query("APPID") String appId);
@QueryMap 複数のクエリパラメータをMap形式でまとめて指定します。 @GET("group/users")<br/>Call<...> groupList(@QueryMap Map<String, String> options);
@Url URL全体を動的に指定します。このアノテーションを使用する場合、Retrofitインスタンスに設定したベースURLは無視されます。 @GET<br/>Call<...> streaming(<strong>@Url</strong> String url);

3. リクエストボディ

POSTやPUTリクエストでサーバーにデータを送信する際に使用します。

アノテーション 説明 使用例
@Body 指定されたオブジェクトをリクエストボディとして送信します。ConverterによってJSONなどにシリアライズされます。 @POST("users/new")<br/>Call<User> createUser(<strong>@Body</strong> User user);
@FormUrlEncoded
@Field
@FieldMap
フォーム形式(application/x-www-form-urlencoded)でデータを送信します。@FormUrlEncodedをメソッドに付与し、各項目を@Fieldまたは@FieldMapで指定します。 <strong>@FormUrlEncoded</strong><br/>@POST("user/edit")<br/>Call<User> updateUser(<strong>@Field("name")</strong> String name, <strong>@Field("email")</strong> String email);
@Multipart
@Part
@PartMap
マルチパート形式(multipart/form-data)でデータを送信します。ファイルのアップロードなどで使用されます。@Multipartをメソッドに付与し、各パートを@Partまたは@PartMapで指定します。 <strong>@Multipart</strong><br/>@PUT("user/photo")<br/>Call<User> updateUser(<strong>@Part("photo")</strong> RequestBody photo, <strong>@Part("description")</strong> RequestBody description);

4. ヘッダー操作

リクエストヘッダーを静的または動的に設定します。

アノテーション 説明 使用例
@Headers 静的なヘッダーをメソッドに付与します。複数指定可能です。 <strong>@Headers({</strong><br/> "Accept: application/vnd.github.v3.full+json",<br/> "User-Agent: Retrofit-Sample-App"<br/><strong>})</strong><br/>@GET("users/{username}")<br/>Call<...> getUser(...);
@Header 動的なヘッダーをメソッドの引数として渡します。 @GET("user")<br/>Call<User> getUser(<strong>@Header("Authorization")</strong> String authorization);
@HeaderMap 複数の動的なヘッダーをMap形式でまとめて指定します。 @GET("user")<br/>Call<User> getUser(<strong>@HeaderMap</strong> Map<String, String> headers);

応用編:さらに便利に使いこなす

基本的な使い方をマスターしたら、次はより実践的なテクニックを見ていきましょう。

1. Interceptorによる共通処理

Interceptor(インターセプター)は、Retrofitが内部で利用しているOkHttpの機能で、全てのリクエストとレスポンスに割り込んで共通の処理を挟むことができます。 これは非常に強力で、様々な用途に活用できます。

Interceptorの主な利用例

  • 共通ヘッダーの付与: 全てのリクエストに認証トークン(Authorizationヘッダー)やAPIキーを追加する。
  • リクエスト/レスポンスのロギング: デバッグ目的で、通信内容を詳細にログ出力する。
  • リクエストの書き換え: 送信するリクエストに共通のパラメータを追加したり、ヘッダーを書き換えたりする。
  • エラーハンドリング: 特定のHTTPエラーコード(例: 401 Unauthorized)を検知して、認証トークンのリフレッシュ処理を自動で行う。
  • リトライ処理: 通信失敗時に、自動でリクエストを再試行する。

Interceptorを作成し、OkHttpClientをビルドする際に`addInterceptor()`メソッドで追加します。

// 例:全てのヘッダーに認証トークンを付与するInterceptor
class AuthInterceptor implements Interceptor {
    private final String authToken;

    public AuthInterceptor(String token) {
        this.authToken = token;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request(); // 元のリクエストを取得
        Request.Builder builder = originalRequest.newBuilder()
                .header("Authorization", "Bearer " + authToken); // ヘッダーを追加
        Request newRequest = builder.build();
        return chain.proceed(newRequest); // 新しいリクエストで通信を続行
    }
}

// OkHttpClientにInterceptorを追加
OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(new AuthInterceptor("YOUR_TOKEN_HERE"))
        .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) // ログ出力用Interceptor
        .build();

// Retrofitインスタンス生成時にこのOkHttpClientを指定
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(client) // カスタマイズしたOkHttpClientを設定
        .addConverterFactory(MoshiConverterFactory.create())
        .build();

2. エラーハンドリング

API通信にはエラーがつきものです。Retrofitにおけるエラーハンドリングは、主に「通信自体の失敗」と「サーバーからのエラーレスポンス」の2つに大別されます。

  • 通信自体の失敗: ネットワークに接続されていない、DNSの名前解決ができない、タイムアウトしたなどの場合です。この場合、`onFailure()`コールバックが呼ばれるか、`execute()`が`IOException`をスローします。
  • サーバーからのエラーレスポンス: 通信には成功したものの、HTTPステータスコードが4xx(クライアントエラー)や5xx(サーバーエラー)だった場合です。この場合、`onResponse()`コールバックが呼ばれますが、`response.isSuccessful()`が`false`を返します。

エラーレスポンスの詳細は、`response.code()`でステータスコード、`response.message()`でメッセージ、`response.errorBody()`でエラーレスポンスのボディを取得できます。

call.enqueue(new Callback<...>() {
    @Override
    public void onResponse(Call<...> call, Response<...> response) {
        if (response.isSuccessful()) {
            // 成功処理
        } else {
            // エラーレスポンスの処理
            int code = response.code();
            String message = response.message();
            ResponseBody errorBody = response.errorBody();
            try {
                if (errorBody != null) {
                    String errorJson = errorBody.string();
                    // エラー用のJSONをパースしてユーザーにメッセージを表示するなど
                }
            } catch (IOException e) {
                // ...
            }
        }
    }
    // ... onFailure
});

3. 非同期処理との連携 (Kotlin Coroutines / RxJava)

コールバックベースの非同期処理は、処理が複雑になると「コールバック地獄」に陥りがちです。 Retrofitは、Kotlin CoroutinesやRxJavaといったモダンな非同期処理ライブラリとシームレスに連携できます。

Kotlin Coroutinesとの連携

Kotlin Coroutinesを使えば、非同期コードを同期コードのように直感的に書くことができます。 Retrofit 2.6.0以降では、特別なアダプターなしでCoroutinesをサポートしています。

インターフェースのメソッドに`suspend`修飾子を付け、戻り値の`Call`を外すだけです。

// インターフェースの定義 (suspend関数)
interface GitHubService {
    @GET("users/{user}/repos")
    suspend fun listRepos(@Path("user") user: String): List<Repo>
}

// 呼び出し側 (CoroutineScope内)
viewModelScope.launch {
    try {
        val service = ApiClient.getClient().create(GitHubService::class.java)
        val repos = service.listRepos("google") // 同期的に見えるが、中断・再開される
        // 成功時の処理
    } catch (e: Exception) {
        // エラーハンドリング (HttpExceptionやIOExceptionなど)
    }
}

RxJavaとの連携

RxJavaは、リアクティブプログラミングを実現するためのライブラリで、データの流れをストリームとして扱います。 Retrofitと組み合わせることで、複雑な非同期処理やイベント処理を宣言的に記述できます。

RxJavaと連携するには、`adapter-rxjava3`(または`rxjava2`)の依存関係を追加し、Retrofitインスタンス生成時に`RxJava3CallAdapterFactory`を追加します。

// build.gradle
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
// インターフェースの定義 (戻り値をObservableやSingleにする)
public interface GitHubService {
    @GET("users/{user}/repos")
    Single<List<Repo>> listRepos(@Path("user") String user);
}

// Retrofitインスタンスの生成
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(MoshiConverterFactory.create())
        .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // RxJava3用のアダプターを追加
        .build();

// 呼び出し側
GitHubService service = retrofit.create(GitHubService.class);
service.listRepos("google")
       .subscribeOn(Schedulers.io()) // 通信はI/Oスレッドで
       .observeOn(AndroidSchedulers.mainThread()) // 結果の受け取りはメインスレッドで
       .subscribe(
           repos -> { /* 成功時の処理 */ },
           throwable -> { /* エラー時の処理 */ }
       );

まとめ

Retrofitは、JavaおよびKotlinにおけるHTTP通信を劇的に簡素化し、堅牢にするための強力なツールです。アノテーションベースの宣言的なAPI定義、柔軟なConverterによるデータ変換、Interceptorによる共通処理の注入、そしてKotlin CoroutinesやRxJavaとの優れた連携機能により、現代のアプリケーション開発に不可欠なライブラリとなっています。

最初は覚えることが多いように感じるかもしれませんが、基本的な3ステップを理解し、アノテーションの使い方に慣れれば、驚くほど効率的にAPIクライアントを実装できるようになるでしょう。この記事が、あなたのRetrofitマスターへの道のりの一助となれば幸いです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です