使用するライブラリ
Retrofit
http通信を簡単に実現するためのライブラリ。JavaやKotlinのインターフェースだけでAPI呼び出しをすることができる。
- httpリクエストを簡単に実装できる
- サーバーからのレスポンスを自動的にデータクラスに変換できる
- 通信エラー処理やリトライ制御もできる
Gson
JSONデータとオブジェクトの相互変換をするためのライブラリ。Googleが提供している
- JSON→データクラスへの変換(デシリアライズ)
- データクラス→JSON文字列への変換(シリアライズ)
- ネスト構造のある複雑なJSONにも対応可能
Rertofitは通信を担当し、Gsonはデータの変換を担当する。
実装イメージ
TheMealDBというAPIを使用する場合を想定して実装
gradleファイルに以下を追加して、プロジェクトにライブラリを追加する
build.gradle
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
続けて、API通信に必要なデータクラス、通信処理をじs
Category.kt
// カテゴリが持つデータを定義
data class Category(
val idCategory: String,
val strCategory: String,
val strCategoryThumb: String,
val strCategoryDescription: String
)
// API通信のレスポンスを定義
data class CategoriesResponse(val categories: List<Category>)
ApiService.kt
// API通信のためのインターフェースを定義
interface ApiService {
//`categories.php`エンドポイントに対してGETリクエストを送信する
// suspend関数で定義をしているのでコルーチン内で呼び出す
@GET("categories.php")
suspend fun getCategories(): CategoriesResponse
}
// Retrofitインスタンスを作成する
private val retrofit = Retrofit.Builder()
// APIのペースURLを設定。この後ろにエンドポイントが付く
.baseUrl("https://www.themealdb.com/api/json/v1/1/")
// Gsonを使用してJSONデータクラスに変換する
.addConverterFactory(GsonConverterFactory.create())
.build()
// ApiServiseインターフェースを実体化して、APIリクエストを使えるようにする
val recipeService = retrofit.create(ApiService::class.java)
CategoryViewModel.kt
class CategoryViewModel: ViewModel() {
private val _categoriesState = mutableStateOf(RecipeState())
val categoriesState: State<RecipeState> = _categoriesState
// ViewModelが作成されたときにデータ取得処理を行う
init {
fetchCategories()
}
private fun fetchCategories() {
// 非同期処理(コルーチン)内でAPI通信を行う
viewModelScope.launch {
try {
// 正常にデータ取得した場合、RecipeStateを更新してデータ取得が完了したことをUIに知らせる
val response = recipeService.getCategories()
_categoriesState.value = _categoriesState.value.copy(
loading = false,
list = response.categories,
error = null
)
} catch (e: Exception) {
_categoriesState.value = _categoriesState.value.copy(
loading = false,
error = "データ取得エラー:${e.message}"
)
}
}
}
data class RecipeState(
val loading: Boolean = true,
val list: List<Category> = emptyList(),
val error: String? = null
)
}
API通信自体はsuspendで定義されているため、コルーチンを使用して呼び出す必要があるためviewModel.launchを使用して非同期処理を行っています。
suspendを使う意味は?
その関数が一時停止することで、非同期で安全に実行できるようにするため。
API通信などの時間がかかる処理は同期的に処理が走ると、アプリが止まってしまいユーザーにとって使いにくいアプリになってしまうため、スムーズに処理を行うために非同期で処理を行う。