くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

Retrofitの基本的な使い方

今回はRetrofitの基本的な使い方について書きたいと思います。

Android開発でAPI連携を行う場合に必ずと言っていいほど使うと思います。たまにハマってしまうので、まとめようかと思います。

Retrofitは、APIから取得するJSONデータを、オブジェクトにしてアクセスできるようにするまでを面倒見てくれるHTTP clientです。

通信はOkhttpというライブラリを合わせて使うことが多いと思います。今回はOkHttpの説明は省きたいと思います。

では使い方をみていきましょう!!

Retrofitの使い方

使い方はかなりシンプルです。

  1. gradleでの設定追加
  2. Interface実装
  3. HttpClient実装

gradleの設定追加に関しては下記自分が書いた記事をみてください!

RxjavaとRetrofitで非同期通信を行う

今回は2の「Interface実装」を少し詳しくみていきます。

リクエストメソッド

下記が1番シンプルなパターンです。

@GET("api")
val api(): Call<Response>

上記の場合だと、実行されるAPIは「http://○○○○○○○○○○○○○○○○/api」という形になります。ここでは説明を省いてましたが、URLのapi以前はHttpClient実装で定義することが多いと思います。

@GETをつけることでGETメソッドであることを明示します。

また、下記のように直接パラメータをつけることも可能です。

@GET("api/list?sort=desc")
val api(): Call<Response>

URLを動的に扱う

URLを動的に扱う場合は下記のようにします。

@GET("api/{id}/list")
fun getList(@Path("id") groupId : Int) : Call<Response>

{}で囲んで命名し、@PathをNameと一緒に定義することで渡したパラメータがURLパスの中に代入されます。 今回の場合はidを動的にしてURLでその値を受け取ってリクエストを作ります。

また、@Pathの他に@Queryというものもあります。

@GET("api")
fun getList(@Query("id") groupId : Int) : Call<Response>

こうするとAPIのURLは「http://○○○○○○○○○○○○○○○○/api?id=○○」になります。後ろにをつけてパラメータを追加する形になります。 GETリクエストで多い形式です。

リクエストボディ

先ほどのURLの末尾にをつけてパラメータを追加する物とは別に、インスタンスがもつ要素をJson形式に変換してパラメータに追加する形式があります。 その場合はインスタンスクラスを引数にするインターフェースを用意し、@Bodyをつけます。

これはPOSTやPUTなどでよく使われます。

場合によりますが、GETとPOSTで@Bodyを使うか@Queryを使うか変わってくるので確認しましょう!これで自分は苦労しました。(今回はこれが理由で記事書いてます)

@POST("users/create")
fun createUser(@Body user: User): Call<Response>;

RetorfitではHeaderを定義するることもできます。

@Headers({
    "Accept: application/json",
    "token: AAAAAAA"
})
@GET("api")
val api(): Call<Response>

単体指定、複数指定も可能です。ただ、動的な値を指定する場合は悪鬼のようにするかOkHttpのIntercepterで動的に作成することになると思います。

@GET("api")
val api(@Header("token") token: String): Call<Response>

// もしくはマップ形式で渡すことも可能
@GET("user")
val api(@HeaderMap Map<String, String> headers): Call<Response>

FORM ENCODED

application/x-www-form-urlencoded (key=value) 形式に自動で変換して欲しい場合は@FormUrlEncoded@Filedを使います。

@FormUrlEncoded
@POST("user/edit")
val updateUser(@Field("first_name") firstName :String, @Field("last_name") lastName :String): Call<Response>

@FormUrlEncodedUTF-8エンコードしてしまうので、他の文字コードで POST する必要がある場合は@Bodyを使う必要があります。

MULTIPART

Retrofitは画像やファイルなどマルチパートなデータ送信にも対応しています。 次のように@Multipartをつけ、送りたいデータには@Partあるいは@PartMapをつけます。

@Multipart
@PUT("user/photo")
fun updateUser(@Part("photo") photo: RequestBody): Call<User>

ただ、@Multipart@FormUrlEncodedはContent-Typeが矛盾するので併用できません。

HttpClient実装

最後にHttpClientの実装についても軽く触れます。 Retrofitのフレームワークを使い、APIを定義したInterfaceをインスタンス化します。

val retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

val service = retrofit.create(GitHubService.class)

このserviceがInterface(APIを定義したもの)であり、実際にAPIコールを行う際に利用します。

また、RetrofitではhttpClientの実装としてokhttpを使用するのが一般的ですが、自分でOkHttpのインスタンスを指定することが出来ます。

val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(new HttpLoggingInterceptor().setLevel(Level.BODY))
        .build();

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
 .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build();

val service = retrofit.create(GitHubService.class)

今回はログ出力の設定を入れているだけですが、ヘッダーの設定やその他いろんな設定を加えることができます。

Gsonについて

HttpClient実装でGsonConverterFactory.create()というファクトリーメソッドを設定しました。
今回Gsonを使用するので、ファクトリーメソッドを設定しないとJSONをオブジェクトに変換できないためです。

ここでGsonについて補足しようと思います。

Gsonは、Googleが提供するJSONデータとオブジェクトを相互に変換するためのライブラリです。 具体的には、JSONデータをレスポンスのクラスにマッピングした状態に変換するときに使用するクラスです。

オブジェクトをマッピングする場合には次のようにして使用します。

data class TestData(
    var firstName: String,
    var secondName: String
)

val strJson = "{firstName=A, secondName=B}"
val testData = Gson().fromJson(strJson, TestData::class.java)
val firstName = testData.firstName

リストをマッピングする場合には次のように使用します。

val strJson = "[{firstName=A, secondName=B},{firstName=AAA,secondName=BBB}]"
val listType = object : TypeToken<List<TestData>>() {}.type
val testDataList = Gson().fromJson<List<TestData>>(strJson, listType)
val firstName = testDataList[0].firstName

実際にAPIで使用する場合にはマッピングするクラスを指定すればいいだけなので、上記のような処理は基本的には行わないですが、 Jsonのレスポンスをマッピングする場合には上記のようにしてマッピングをするというのを覚えておくといいかもしれません。

参照

Retrofit公式ドキュメント

Android Retrofit2 with Kotlin