発生したイベントをキューに貯めて順次処理して行きたい場合でViewModelを使用時の使えそうな実装パターンとしてイベント消費型UIイベントがあります
デベロッパーの説明だけでは足らない気もするので対応してみます
以下のサイトの内容の改造版みたいな感じです
https://star-zero.medium.com/viewmodel%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E5%AE%9F%E8%A3%85-74dd814deb97
Eventクラスは消費時に判別するためのユニークなIDをつけます
ダミーのイベント発生のstubとイベント消費のためのconsumeEventを実装します
ダミーのイベントはテストのため連続で5つ発生するようにしてます
@Stable
sealed class Event {
val id = UUID.randomUUID().toString()
data class Message(val message: String) : Event()
}
class AppViewModel : ViewModel() {
private val _uiEvents = MutableStateFlow<List<Event>>(emptyList())
val uiEvents = _uiEvents.asStateFlow()
fun stub() = viewModelScope.launch {
val message = listOf(
"おはよう",
"こんにちわ",
"こんばんわ",
)
repeat(5) {
val msg = message.random()
_uiEvents.update {
it + Event.Message(msg)
}
}
}
fun consumeEvent(event: Event) {
_uiEvents.update {
e -> e.filterNot { it.id == event.id }
}
}
}
LaunchedEffectでuiEventsに変化(追加/消費)が発生した時に最初に追加されたイベントを1つ取得して処理していきます
ここではイベント発生後の処理の完了を待つ変わりにdelayしてます
@Composable
fun AppContent(viewModel: AppViewModel = viewModel()) {
val uiEvents by viewModel.uiEvents.collectAsState()
var message by remember { mutableStateOf("") }
LaunchedEffect(uiEvents) {
if (uiEvents.isNotEmpty()) {
when (val event = uiEvents.first()) {
is Event.Message -> {
message = "${event.message}(${event.id})"
Log.d("App", message)
launch {
delay(1_000)
viewModel.consumeEvent(event)
}
}
}
}
}
Column {
Button(onClick = { viewModel.stub() }) {
Text("Start")
}
Text(message)
}
}
古典的なやり方だとキュークラス実装したり再起処理実装したりとごちゃごちゃやってたけど簡単にスッキリとした実装となりました
Android Studio Giraffe 2022.3.1 built on June 29, 2023