😊
初めてのStateFlowと注意点
Noteの記事を加筆修正したものです。
StateFlowとは?
アプリケーションで更新可能な状態を表現するためにFlowを便利に使えるようにする必要があります。
今回の変更では、StateFlow -- 状態を表す更新可能な値であり、FlowであるStateFlowが導入されました。様々なニーズに柔軟に対応できるように設計されています。
StateFlow<T>インターフェイスは、現在の値へのアクセスを与える読み取り専用のビューであり、値への更新を収集するためのFlow<T>を実装しています。
MutabaleStateFlow<T>インターフェイスは、値の変更操作を追加します。
MutableStateFlow(x)のコンストラクタ関数が提供されています。
これは、与えられた初期値を持つMutableStateFlowの実装を返します。
値への非反応的な高速アクセスが必要な場合はStateFlow<T>として、値への更新の反応的な表示のみが必要な場合はFlow<T>として外部に公開することができます。
コアとなるステートフローAPIは以下のようにまとめることができます。
www.DeepL.com/Translator(無料版)で翻訳しました。
値の取得ができたりMutableStateFlowなら値のセットもできるみたいですね。
完全に理解できたところで触っていきます。
ViewModel
class SubFragmentViewModel(val id: Int, val repository: Repository) : ViewModel() {
val _message = MutableStateFlow<String?>(null)
val message: StateFlow<String?> get() = _message
val flowMessage: Flow<String?> get() = _message
fun setMessage(message: String?) {
_message.value = message
}
fun loadMessage() {
Log.d("AAAAAAAAAAAAAA", message.value)
}
fun loadMessage2() {
viewModelScope.launch {
message.collect {
Log.d("BBBBBBBBBBBBBBB", it)
}
}
}
fun loadMessage3() {
viewModelScope.launch {
flowMessage.collect {
Log.d("CCCCCCCCCCCCCCCC", it)
}
}
}
}
MutableStateFlowの_message
とそれを参照してStateFlowにキャストしているmessage
、FlowにキャストしているflowMessage
を用意しました。
setMessage()
を呼ぶ事でMutableStateFlowの_messageに文字列をセットします。
セットされた値を読み込む物はloadMessage()
を用意し、
StateFlowのmessageからgetValueする、collectして変更があれば読み込むものと
FlowのflowMessageからcollectして値を読み込む物を用意しました。
Fragment
class SubFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.setMessage("TEST")
viewModel.setMessage("TEST2")
viewModel.setMessage("TEST3")
viewModel.loadMessage()
viewModel.loadMessage2()
viewModel.loadMessage3()
viewModel.setMessage("TEST4")
viewModel.setMessage("TEST5")
}
}
FragmentではViewModelで用意した文字列をセットするsetMessage
を3回読んだ後に
値を読み込むloadMessage
を呼び、最後に再度文字列をセットした時の挙動を確認する事にしました。
動作結果
2020-05-20 07:12:13.503 29370-29370/com.example.masterofkoin D/AAAAAAAAAAAAAA: TEST3
2020-05-20 07:12:13.559 29370-29370/com.example.masterofkoin D/BBBBBBBBBBBBBBB: TEST5
2020-05-20 07:12:13.562 29370-29370/com.example.masterofkoin D/CCCCCCCCCC
StateFlowのgetValueは呼び出した時点の値しか取れない事は予想通りでしたが、
Collectした時に最新の値しか取れないのは予想外でした。
再検証(delayを入れて値をセット)
fun pushMessage() {
viewModelScope.launch {
setMessage("TEST")
setMessage("TEST2")
setMessage("TEST3")
delay(500)
setMessage("TEST4")
setMessage("TEST5")
}
}
メッセージのセット中にdelayを入れてみました。
2020-05-20 08:08:07.574 6563-6563/com.example.masterofkoin D/BBBBBBBBBBBBBBB: TEST3
2020-05-20 08:08:07.575 6563-6563/com.example.masterofkoin D/CCCCCCCCCCCCCCCC: TEST3
2020-05-20 08:08:08.075 6563-6563/com.example.masterofkoin D/BBBBBBBBBBBBBBB: TEST5
2020-05-20 08:08:08.076 6563-6563/com.example.masterofkoin D/CCCCCCCCCCCCCCCC: TEST5
無事にTEST3/TEST5の2回Collectする事ができました。
まとめ
毎回必ず変更を検知したい場合には注意が必要。
Discussion