【Kotlin/Android Studio】Retrofitの使い方!HTTP通信でAPIを取得する

この記事からわかること
- Android Studio/KotlinでRetrofitの使い方
- HTTP通信を実装してAPIを取得する方法
- QiitaAPIを使用した記事取得アプリの実装方法
- HTTPメソッドアノテーションの種類
- 使用できるコンバーターの種類
- No type arguments expected for class Callの解決法
- java.lang.IllegalArgumentException: Unable to create converter for class com.example.retrofit.Article for method QiitaService.fetchDataの解決法
index
[open]
\ アプリをリリースしました /
参考文献:公式リファレンス:Retrofit
環境
- Android Studio:Flamingo
- Kotlin:1.8.20
Retrofitとは?
RetrofitはAndroidアプリ開発で簡単にHTTP通信を実装できるライブラリです。開発したのはSquare社という会社でオープンソースのライブラリになっています。アプリ開発ではよく登場するAPI(RESTful API)を利用するためにはRetrofitは欠かせない存在です。
HTTP通信を実装するにはややこしいAPIのエンドポイントを把握したり、サーバーのエラー処理、レスポンスで受け取ったJSONのマッピングなど複雑でめんどくさい作業が多いです。それをRetrofitを使うことで簡単に実装できるようになっているので使い方を見ていきます。
HTTP通信部分の内部的な実装は同じSquare社が開発しているOkHttpが使われています。
Retrofitの実装方法
実際に実装するために今回はQiitaの記事情報を取得できるAPIを使用していきます。APIのエンドポイントは以下のURLでここから記事情報が以下のJSON形式で返ってきます。
記事(新着順20個):https://qiita.com/api/v2/items
レスポンスで受け取るJSON
[{
"id": "記事の一意のID",
"title": "記事のタイトル",
"body": "記事の本文",
"created_at": "記事の作成日時",
"updated_at": "記事の更新日時",
"user": {
"id": "ユーザーの一意のID",
"name": "ユーザー名",
"profile_image_url": "ユーザーのプロフィール画像のURL"
},
// .....他にもさまざまな情報
}]
ちなみにSwiftで実装した記事は↓こちらです。
実装の流れ
最初に実装の流れを確認しておきます。
- 依存関係の追加
- インターネットの利用を可能にする
- JSONにマッピングするデータクラスの作成
- インターフェースの作成
- ベースURLの定義
- Retrofit.Builderでインスタンスの作成
- MainActivityで非同期による通信の実装
今回のプロジェクトの全体はGitHubに上げていますので参考にしてください。
必要になるクラスなど
RetrofitでAPI取得アプリを実装するにあたって必要となるクラスなどをまとめておきます。
- BaseUrl:APIのベース
- データクラス:JSONマッピング用クラス
- ConverterFactory:JSON⇄データクラスを相互に交換するクラス
- インターフェース:HTTP通信メソッドの定義とパラメータ構築
- Retrofitインスタンス:管理クラス
依存関係の追加
RetrofitをAndroid Studio内で使用できるようにするには依存関係を追加する必要があります。また一緒にRetrofitが公式サポートしているGSON(JSONをマッピングするライブラリ)も導入しておきます。
dependencies {
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.google.code.gson:gson:2.8.5'
}
インターネットの利用を可能にする
AndroidアプリからWeb通信をさせるためにはまず設定を変更する必要があります。デフォルトではインターネットアクセスが許可されていないので「AndroidManifest.xml」に<uses-permission android:name="android.permission.INTERNET" />
を追加しておきます。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
+ <uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
JSONにマッピングするデータクラスの作成
続いてJSONで返ってくるデータをKotlinで扱えるようにデータクラスを定義しておきます。ここのプロパティ名はJSONのキー値と同じにしておきます。またJSONの構造にあったようなクラス関係に定義する必要があります。
data class Article(
val id: String,
val title: String,
val body: String,
val created_at: String,
val updated_at: String,
val user: User
)
data class User(
val id: String,
val name: String,
val profile_image_url: String
)
インターフェースの作成
続いてインターフェースを定義します。XxxxxService
という名前にしていることが多いのでQiitaService
としておきました。ここでは実際にリクエストを送るメソッドを定義していきます。例えばGETメソッドを定義したい場合は以下のようになります。驚くほど簡素な定義ですがあとはRetrofitが上手いことやってくれるようです。
// ↓ちなみに自動補完だと異なるimport分が入ってしまいエラーになるので注意
// import android.telecom.Call // ×
import retrofit2.Call // ⚪︎
import retrofit2.http.GET
interface QiitaService {
@GET("items")
fun fetchData(): Call<List<Article>>
}
ここでポイントとなるのが@GET
アノテーションとCall
クラスです。GETメソッドを定義する際は@GET
アノテーションをPOSTメソッドを定義する際は@POST
アノテーションという具合に付与することであとはRetrofitに任せておけます。また今回定義したfetchData
メソッドには具体的な実装は何もありませんが、これだけでHTTP通信を実装する処理は完了です。
@GET
アノテーションの引数に渡しているのはAPIパスの続きです。今回ではhttps://qiita.com/api/v2/
がベースとなり、items
が変化させる可能性のあるパス部分になります。今回はパスのみでしたがクエリパラメータなども指定可能です。
ベースURLの定義
ではベースとなるURLも定義しておきましょう。これはどこでも良いですが先ほどの「QiitaService.kt」ファイルのトップレベルにpublic
で定義しておきました
public const val BASE_URL = "https://qiita.com/api/v2/"
Retrofit.Builderでインスタンスの作成
「MainActivity.kt」に移ってプロパティにRetrofitインスタンスを生成しておきます。Retrofit.Builder
メソッドからインスタンスを生成でき、addConverterFactory
メソッドで変換してくれるConverterFactoryを指定します。今回はGSONなのでGsonConverterFactory.create()
でOKです。あとはAPIのベースURLを私、build
を実行します。
class MainActivity : AppCompatActivity() {
private val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
// 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
MainActivityで非同期による通信の実装
最後にMainActivity内からHTTP通信を実行します。今回は取得したJSONをTextView
に格納するようにしておきました。先にコードを見てみます。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val text:TextView = findViewById(R.id.json_text)
// 以下Retrofit
val service = retrofit.create(QiitaService::class.java)
val get = service.fetchData()
GlobalScope.launch(Dispatchers.IO) {
var responseBody = get.execute()
var weatherApiResponse = responseBody.body()?: throw IllegalStateException("bodyがnullだよ!")
runBlocking {
launch(Dispatchers.Main) {
text.setText(weatherApiResponse.toString())
}
}
}
}
}
まず最初にQiitaService
クラスをクラスリテラル(::class.java
)を使用して取得します。
val service = retrofit.create(QiitaService::class.java)
次に実行したいHTTP通信を定義したメソッドを呼び、Call
インスタンスを取得します。
val get = service.fetchData()
実際の通信処理はメインスレッドでは行わないようにKotlin CoroutinesでDispatchers.IOを指定しexecute
メソッドで実際に実行しています。また通信処理の実行はexecute
メソッドを使用すると同期的にenqueue
メソッドを使用すると非同期的にHTTPリクエストが実行されます。
おすすめ記事:【Android Studio】Kotlin Coroutinesの使い方!非同期処理とスレッド
GlobalScope.launch(Dispatchers.IO) {
var responseBody = get.execute()
var weatherApiResponse = responseBody.body()?: throw IllegalStateException("bodyがnullだよ!")
runBlocking {
launch(Dispatchers.Main) {
text.setText(weatherApiResponse.toString())
}
}
}
execute
メソッドの結果からbody
メソッドでJSONデータを参照できるので値がnull
であれば例外をスローし、null
でなければメインスレッドに切り替えてUIの更新をしています。

toString
メソッドを使用して無理やりテキストとして表示しましたが実際にはArticleクラスのList形式に変換されているのでそのままリサイクルビューなどに渡すだけで簡単に表示することが可能です。
返り値を必要としないPOSTメソッドを実装する
返り値を必要としないPOST送信を実装する場合も見てみます。POST
の場合は何かしらURLにデータを含ませることが多いと思いますが、パスに含めたい場合は@POST("{token}/{title}/{msg}")
でURL部分を構築し、@Path
を使用して引数とパスを繋ぎます。
interface NotifyApiService {
@POST("{token}/{title}/{msg}")
suspend fun sendNotification(
@Path("token") token: String,
@Path("title") title: String,
@Path("msg") msg: String
): Response<Unit>
}
呼び出す際は以下のようにすっきり記述することができます。
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.build()
val apiService = retrofit.create(NotifyApiService::class.java)
runBlocking {
try {
val response = apiService.sendNotification(token, title, msg)
if (response.isSuccessful) {
println("Notification sent successfully")
} else {
println("Failed to send notification: ${response.code()}")
}
} catch (e: Exception) {
println("Error: ${e.message}")
}
}
HTTPメソッドアノテーションの種類
アノテーション | 概要 |
---|---|
@GET | 引数にはパスの続きやクエリパラメータ、プレースホルダ{}を設置して変数渡しが可能 |
@POST | 引数にはパスの続きやクエリパラメータ、プレースホルダ{}を設置して変数渡しが可能 |
@PUT | 引数にはパスの続きやクエリパラメータ、プレースホルダ{}を設置して変数渡しが可能 |
@DELETE | 引数にはパスの続きやクエリパラメータ、プレースホルダ{}を設置して変数渡しが可能 |
@FormUrlEncoded | @POSTなどの前に設定することでフォームエンコードされたデータが送信 |
@Multipart | @POSTなどの前に設定することでマルチパートで送信 |
@Header | HTTP Headerを設定 |
詳細な使い方などは公式サイトを参考にしてください。
エラー集
私がRetrofitを使用する際に発生したエラーを紹介しておきます。
No type arguments expected for class Call
以下のエラーはインターフェース定義時にCallのimport文が間違っていることで発生したエラーです。
No type arguments expected for class Call
Android Studioの自動補完でimport
文が記述されますが、その際にRetrofitのimport retrofit2.Call
が欲しいのに別のimport android.telecom.Call
が入ってしまうようです。
// import android.telecom.Call // ×
import retrofit2.Call // ⚪︎
これはimport
文を書き換えることで解決しました。
java.lang.IllegalArgumentException: Unable to create converter for class com.example.retrofit.Article for method QiitaService.fetchData
以下のエラーはコンバーターが正常に指定されていない場合に発生するエラーのようです。私は最初GSONではなくScalarsConverterFactory
を使用しておりましたがこちらではなぜか以下のエラーが発生しうまくいきませんでした。
java.lang.IllegalArgumentException: Unable to create converter for class com.example.retrofit.Article for method QiitaService.fetchData
Scalarsの依存関係追加コード
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
使用できるコンバーターの種類
- GSON:
com.squareup.retrofit2:converter-gson:$version_retrofit
- Jackson:
com.squareup.retrofit2:converter-jackson:$version_retrofit
- moshi:
com.squareup.retrofit2:converter-moshi:$version_retrofit
- protocol-buffers:
com.squareup.retrofit2:converter-protobuf:$version_retrofit
- ワイヤー:
com.squareup.retrofit2:converter-wire:$version_retrofit
- 単純な XML:
com.squareup.retrofit2:converter-simplexml:$version_retrofit
- JAXB:
com.squareup.retrofit2:converter-jaxb:$version_retrofit
- Scalars:
com.squareup.retrofit2:converter-scalars:$version_retrofit
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。