Arpit Shukla
02/02/2022, 1:36 PMIn some apps, you might have seen ViewModel events being exposed to the UI using Kotlin Channels or other reactive streams. These solutions usually require workarounds such as event wrappers in order to guarantee that events are not lost and that they're consumed only once.
I too (sometimes) use a Channel
to send events from ViewModel to UI as a Flow
which I collect in LaunchedEffect
but I never had to use any
workarounds such as event wrappers in order to guarantee that events are not lost and that they're consumed only onceIt is automatically taken care of while using channels. What exactly did the author mean here?
Adam Powell
02/02/2022, 2:25 PMArpit Shukla
02/02/2022, 2:38 PMLiveData<Event<T>>
pattern which was used as a workaround to guarantee that events are consumed only once. But that wasn't in anyway related to channels. The guide specifically refers to Kotlin Channels (and similar reactive streams). Is there any such thing with Channels that I need to watch out for?Halil Ozercan
02/02/2022, 2:49 PMBroadcastChannel
(now deprecated?) which replays some items to new subscribers.madisp
02/02/2022, 5:35 PMFlow
configurations as wellArpit Shukla
02/02/2022, 7:27 PMAlex Vanyo
02/04/2022, 10:44 PMChannel
is that an element can be received from a Channel
, but the receiver is cancelled before the element is processed, which results in behavior of “at-most-once delivery and handling”
More general solutions that get closer to that “exactly-once delivery and handling” start getting extremely subtle, can depend on dispatcher behavior, and can break due to depending on a very precise dance of ordering (things like adding intermediate filtering or mapping can break assumptions).
It’s possible you’ve avoided those edge cases so far if “at-most-once” is good enough for your use case or if your usage happens to avoid the issue.
Just to re-iterate again, exposing state from a ViewModel (either StateFlow
or Compose State>
) is the simplest to reason about, test, and should be your go-to approach.Arpit Shukla
02/05/2022, 2:56 PMStylianos Gakis
02/10/2022, 6:17 AMAlex Vanyo
02/10/2022, 8:40 PMbake events inside the StateJust to highlight an important mindset here which is fairly subtle: Try to go one step further than just putting the events into a part of the state and thinking about them as events the entire time. So instead of
List<Message>
“here are the list of show message events that haven’t been sent to the UI yet”, think “here are a list of messages pending display to the user”.
The underlying data being stored will be very similar, which admittedly makes this a subtle distinction. When you take the mindset of “here are a list of messages pending display to the user,” there’s a bunch of natural follow-up questions that are important:
• When is a message considered “displayed” to remove it from the list?
• Should the list of messages have a max size if the list keeps growing?
• Should the list of messages be stored (for example, to persist through process death)?
An approach with a channel with events hide those questions. For example, what happens if you receive an event from a channel just before a configuration change happens? Or the user backgrounds the app 10 milliseconds after starting to show the message? Those questions would still be valid even with a different primitive than a Channel
.
The “dance,” as you put it, between the ViewModel and the UI is a bit longer, but it makes the answers to the above questions for your use case explicit and not depend on the exact internal workings of Channel
or another primitive. It doesn’t hide the reality that an AAC ViewModel outlives the UI, and that guaranteeing a longer-lived producer telling a shorter-lived consumer to do something exactly once is very difficult.Stylianos Gakis
02/10/2022, 9:50 PMArpit Shukla
02/11/2022, 4:51 AMSnackbarHostState
in the ViewModel itself and calling showSnackbar
right from the ViewModel?Adam Powell
02/11/2022, 2:28 PM