こんにちは。
今回は、前回紹介したFlowの応用編です。 アプリ開発では「サーバーから取得したデータをDBに保存して、それをUIに即反映する」というパターンが多いですよね。
ここでは、Flow × Room × Retrofit を使って、 「データ取得 → DB保存 → UI自動更新」までの流れを構築していきます。
今回のゴール
今回の流れは以下の通りです:
- Retrofitでサーバーからデータを取得
- Roomにデータを保存
- Roomの
FlowでUIを自動更新
つまり、「UI → ViewModel → Repository → Retrofit/Room」のモダン構成ですね。
1. データモデルの定義
@Entity(tableName = "user_table")
data class UserEntity(
@PrimaryKey val id: Int,
val name: String,
val email: String
)
DBで扱うユーザー情報のエンティティです。
2. DaoでFlowを返す
Roomでは、Flowを返すようにすると自動的に変更検知が行われ、 DBのデータが更新されるとFlow経由でUIに通知されます。
@Dao
interface UserDao {
@Query("SELECT * FROM user_table")
fun getAllUsers(): Flow<List<UserEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(users: List<UserEntity>)
}
これだけで「DB変更 → Flow更新 → collectでUIに反映」が可能になります。
3. Retrofitの定義
interface ApiService {
@GET("users")
suspend fun getUsers(): List<UserEntity>
}
object ApiClient {
private const val BASE_URL = "https://example.com/api/"
val retrofit: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
シンプルなRetrofit定義です。 実際のプロジェクトではレスポンス用DTOを作成してEntityにマッピングするのが定石ですが、 ここでは分かりやすく同じ構造にしています。
4. RepositoryでFlowを仲介
class UserRepository(
private val api: ApiService,
private val dao: UserDao
) {
// RoomからFlowで取得
fun getUsers(): Flow<List<UserEntity>> = dao.getAllUsers()
// ネットワークから取得してDBに反映
suspend fun refreshUsers() {
val users = api.getUsers()
dao.insertAll(users)
}
}
Repositoryは、UI層から「Flowを返す+ネットワーク更新を指示される」形になります。
5. ViewModelでcollectする
class UserViewModel(
private val repository: UserRepository
) : ViewModel() {
val userList: StateFlow<List<UserEntity>> =
repository.getUsers()
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
fun refresh() {
viewModelScope.launch {
repository.refreshUsers()
}
}
}
ポイントは、RoomのFlowをStateFlowに変換してUIに公開している点です。 UI側でcollectすれば、DB更新時に自動で再描画されます。
6. UIで表示する
lifecycleScope.launch {
viewModel.userList.collect { users ->
val names = users.joinToString("\n") { it.name }
binding.textView.text = names
}
}
// 手動で更新ボタン
binding.refreshButton.setOnClickListener {
viewModel.refresh()
}
Flowの強みがここで発揮されます。 DBに変更があるたびに、自動的にFlowが新しいリストをemitし、UIが更新されます。
7. 全体の流れをまとめると
ユーザー操作(更新ボタン)
↓
ViewModel.refresh()
↓
Repository.refreshUsers() → RetrofitでAPI呼び出し
↓
UserDao.insertAll() → Room更新
↓
UserDao.getAllUsers() Flowが変化を検知
↓
UIが再描画
つまり、Flowが「UIとDBの橋渡し」をしてくれるということですね。
まとめ
- Roomの
FlowでDB変更を自動検知 - Retrofitで取得したデータをDBに流し込む
- ViewModelでは
stateIn()でUI向けに変換 - UIは
collectするだけで常に最新状態を表示
Flowを組み合わせると、これまで面倒だった「UI更新通知」が不要になります。 LiveDataよりも宣言的で、Composeとの相性も抜群です。
ではまた。


コメント