【Android/Kotlin】RetrofitとREST APIの仕組みをまとめる(基礎編)
こんにちは。この記事では、Androidアプリ開発で頻出の「Retrofit」と「REST API」の関係や使い方について、自分の学習をまとめたものになります。
通信に関して復習
登場人物は主にクライアントとサーバーの2人です。
この2人のやり取り(通信)における決まり事がRESTです。
次にRESTについて詳しく説明していきます。
RESTとは?
REST(Representational State Transfer)とは、Webサービスの設計原則の1つで、「HTTPの仕組みを最大限に活かして、データにアクセスするためのルール」です。
RESTは広く一般的に用いられています。例えば検索やゲームの時など
RESTの特徴
-
URLでリソース(データ)を一意に表現
⇒データの保存場所をURLが担う(住所みたいな) -
HTTPメソッドを使い分ける
操作 メソッド 読み込み GET 作成 POST 更新 PUT 削除 DELETE -
ステートレス(サーバーがクライアントの状態を保持しない)
⇒サーバーに依存しない。つまり、サーバー側でサーバーを横に増やしたり変化が起きてもクライアントは何もしなくていい(柔軟)
クライアント→サーバー
このデータが欲しい⇒(このURL先のデータがGETしたい)
Retrofitとは?
Retrofitは、Square社が提供する「REST API通信を簡単にしてくれるKotlin/Java用のライブラリ」です。
Retrofitがやってくれること
- APIリクエストの送信(GET, POST など)
- サーバーからのレスポンス(JSONなど)をKotlinオブジェクトに変換
- 必要なコードを自動生成
- 非同期処理(Coroutines対応)
Retrofitはクライアントの代わりにサーバーとやり取りしてくれる便利な仲介者。
ここでいう翻訳とは、データはJSON形式やXML形式で管理されていることがほとんどであるため、それらを人間が読みやすい形(Stringなど)に変換することを意味します。
コードでみるRetrofit
"想定するJSONデータ"
{
"id": 1,
"name": "Taro Yamada",
"email": "taro@example.com"
}
ディレクトリ構造
└── com.example.retrofitexample/
├── model/
│ └── User.kt⇒シリアル化
├── network/
│ ├── ApiService.kt⇒APIのインターフェースの定義
│ └── RetrofitClient.kt⇒クライアント側での翻訳等
├── viewmodel/
│ └── UserViewModel.kt⇒状態を管理
└── MainActivity.kt⇒UI管理
詳細
それでは順番にコードの中身を見てみましょう。
まずはmodel/User.kt
に関して
package com.example.retrofitexample.model
import kotlinx.serialization.Serializable
@Serializable
data class User(
val id: Int,
val name: String,
val email: String
)
ここではシリアル化を行っています。
シリアル化とは、ユーザーの入力などをKotlinのオブジェクトとして保持し、それをサーバーに送信できるようにJSON形式へ変換する処理のことです。
続いてnetwork/ApiService.kt
に関してです。
package com.example.retrofitexample.network
import com.example.retrofitexample.model.User
import retrofit2.http.GET
import retrofit2.http.Path
interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") id: Int): User
}
ここでは、Retrofitを使ってAPIのインターフェースを定義しています。
つまりAPIの設計書を定義したようなことです。
ここで定義した関数は、Retrofitのクライアントによって自動的に実装されるため、他のクラス(ViewModel
など)から簡単にAPIを呼び出すことができるようになります。
続いてnetwork/RetrofitClient
についてです。
package com.example.retrofitexample.network
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit
import retrofit2.converter.kotlinx.serialization.asConverterFactory
object RetrofitClient {
private val retrofit = Retrofit.Builder()
.baseUrl("https://example.com/api/")
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
val api: ApiService = retrofit.create(ApiService::class.java)
}
このRetrofitClient
はretrofit
インスタンスを1回だけ生成し保持するシングルトンオブジェクトです。
Builder()
とbuild()
で挟んで、その間に何をするのかをチェーンメソッド形式で書いていきます。
ここでは、addConverterFactory()
を使って JSONとKotlinデータクラスの相互変換(シリアル化/デシリアル化) を行うためのコンバーターを追加しています。
最後にviewmodel/UserViewModel.kt
に関してです。
package com.example.retrofitexample.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.retrofitexample.model.User
import com.example.retrofitexample.network.RetrofitClient
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
sealed interface UserUiState {
data class Success(val user: User): UserUiState
object Loading : UserUiState
object Error : UserUiState
}
class UserViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
val uiState: StateFlow<UserUiState> = _uiState
fun fetchUser(id: Int) {
viewModelScope.launch {
try {
val user = RetrofitClient.api.getUser(id)
_uiState.value = UserUiState.Success(user)
} catch (e: Exception) {
_uiState.value = UserUiState.Error
}
}
}
}
ここでは状態管理がされています。具体的には、コードの冒頭でSuccess
とLoading
とError
と明記して状態を定義します。
また、UserViewModel
クラス内のfetchUser
関数ではviewModelScope.launch
を使って非同期にAPIを呼び出し、取得結果に応じて状態を更新しています。
これにより、UIは状態に応じた表示切替が可能になります。
最後に
これらはhttps://example.com/api/
のような架空のURLを用いていますが、
この公式のドキュメントに沿った内容となっています。良ければこのサイトから本格的に手を動かしながら学習してみてください。
通信に関して概ね理解が深まったと感じています。今後はより発展した内容や、DBに関しても広げていきたいです。