概要
個人AndroidアプリでFirebase Realtime Database(略してRTDB)を使っているが、ValueEventListener
のコールバックで実装する書き方がイマイチ好きではなかった。
そこでcollbackFlow
を使うと、RTDBとの接続をキレイに分離可能かつがStateFlow化できて、スッキリ書くことができた。
また、RTDBとの相性もよくて、値が何度も更新されてもFlowでちゃんと受け取れる。
コード: 状態管理
例として、天気を取得する機能を実装するコードを紹介する。
StateFlowで管理する状態クラス
今回は分かりやすいようにSuccess内の変数をWeatherInfo
にしている
sealed class WeatherUiState {
// 値の取得成功
data class Success(val weatherInfo: WeatherInfo) : WeatherUiState()
// 失敗
data class Error(val message: String) : WeatherUiState()
}
コード: Repository
RTDBに接続して値を取得してくる
class WeatherRepository(private val dbRef: DatabaseReference) {
/**
* 天気を取得
*/
@ExperimentalCoroutinesApi
fun fetchWeather(): Flow<WeatherUiState> = callbackFlow { // callbackFlowの戻り値はFlow型
// DBへの接続
dbRef.addValueEventListener(object : ValueEventListener {
// 正常に取得できた場合
override fun onDataChange(snapshot: DataSnapshot) {
snapshot.getValue(WeatherInfo::class.java)?.let { weather ->
// offerで購読側に値を流せる
offer(WeatherUiState.Success(weather))
}
}
// エラーの場合
override fun onCancelled(error: DatabaseError) {
offer(WeatherUiState.Error(error.message))
}
})
awaitClose {
// 後始末はここでするが、DatabaseReferenceは自動で破棄されるで、やる事なし
}
}
コード: ViewModel
RepositoryのStateFlowを購読するだけ。
class WeatherViewModel : ViewModel() {
private val _weather = MutableLiveData<WeatherInfo>()
val weather :LiveData<WeatherInfo> = _weather
@ExperimentalCoroutinesApi
fun fetchWeather() {
viewModelScope.launch {
// StateFlowの購読
weatherRepository.fetchWeather().collect { state ->
when (state) {
// 成功
is WeatherUiState.Success -> _weather.value = state.weatherInfo
// エラー処理
is WeatherUiState.Error -> // 画面にエラー通知など
}
}
}
}
感想
Flowの学習もかねてcallbackFlow
を触ったが、すごくシンプルかつスッキリ書けていいね。
今回はFirebase RTDBと組み合わせたが、Firestoreにも同じように使えるはず。
あと、位置情報やBLEスキャンにcallbackFlowを使っている記事もあるので、けっこう便利に使えそう