elizarov
06/08/2020, 12:29 PMChannel
) how do you work around the fact that Channel.receive
can get a message in an activity that is already destroyed and thus attempt to work with it will cause an IllegalStateException
? (see this demo https://pl.kotl.in/Rs18kihuJ) The same problem happens when you use Channel.receiveAsFlow
and then collect from the flow (see this demo https://pl.kotl.in/-ehZrPMl4).
😱 I never knew it could happen! All my code is broken!
👍 It never happens to me because I use immediate
dispatcher (Android provides that by default) and I always post from the main thread, so between the event is posted and is being processed my activities cannot get destroyed.
👉 I do something else (comment in 🧵)streetsofboston
06/08/2020, 12:43 PMwhenStarted
or whenResumed
when collecting the Flow (uses a pausing dispatcher)
https://developer.android.com/reference/kotlin/androidx/lifecycle/package-summary#(androidx.lifecycle.Lifecycle).whenStarted(kotlin.coroutines.SuspendFunction1)bezrukov
06/08/2020, 12:52 PMelizarov
06/08/2020, 1:29 PMwhenStarted
does prevent IllegalStateException
for sure. But here is a problem with it. If you posted an event an activitity that was collecting using whenStarted
and is now destroyed, then it will receive an event from the channel, but it will get cancelled before having a chance to process the event. Thus the event will be lost. However, this can only happen if you use non-immediate dispacher or post events from a background thread. If you are doing neither, then you don’t even need whenStarted
in the first place.streetsofboston
06/08/2020, 1:33 PMwhenStarted/Resumed
is used, the navigation doesn’t happen, until the user foregrounds the app again. This is fine (like event-LiveData).
We still need whenStarted
or whenResume
, because the nav-event should not happen while the activity is in the background.streetsofboston
06/08/2020, 1:34 PMwhenStarted
does not cancel the CoroutineScope when the activity moves to a STOPPED state. It pauses the dispatcher instead.elizarov
06/08/2020, 1:40 PMbezrukov
06/08/2020, 1:40 PMAdam Powell
06/08/2020, 1:48 PMstreetsofboston
06/08/2020, 1:50 PMviewModelScope
and whenStarted
is attaching a pausing dispatcher (withContext(PausingDispacther())
).
When rotating the device, the viewModelScope
is not canceled.Adam Powell
06/08/2020, 1:53 PMelizarov
06/08/2020, 2:04 PMviewModelScope
(that is not cancelled) using whenStarted { … }
then it should be fine. The event you received will wait until the activity is restarted.elizarov
06/08/2020, 2:07 PMEvent
wrapper object does for you — makes it a part of your state. Or do you have some other solution in mind here?streetsofboston
06/08/2020, 2:07 PMviewModelScope
and not the lifecycle-scope of the activity/fragment.Adam Powell
06/08/2020, 2:26 PMEvent
wrapper instead of reducing it to a domain-specific state causes some difficulties. The example from the article kind of works because LiveData
is always conflated, and it still doesn't handle the queue of messages to be addressed by the user very wellelizarov
06/08/2020, 2:34 PMLiveData
, coroutines have a rich set of tools that go beyond representing state (you have channels for one, that are directly designed for events), so you don’t have to take a solution designed to represent state and work around it (even though you can). What I’m trying to figure out is whether existing tools we provide are clear and can be used to solve the problem in a concise way or if we need to be adding something/anything else to make developers’ lives easier, and/or just give them more guidance.Adam Powell
06/08/2020, 2:35 PMEvent
wrapper pattern would be an unfortunate thing to carry into kotlinx.coroutines since I think there are better tools there alreadyAdam Powell
06/08/2020, 2:36 PMAdam Powell
06/08/2020, 2:38 PMAdam Powell
06/08/2020, 2:39 PMMutex
forms a queue of messages for the user to address, if a condition that something wanted to notify the user about is no longer relevant, simply cancelling the call to serve
removes it, or causes it to disappear if it's currently being shown.Adam Powell
06/08/2020, 2:41 PMlouiscad
06/08/2020, 2:42 PMdelay
aletrnative that pauses when lifecycle is not resumed
(or started
?)Adam Powell
06/08/2020, 2:43 PMAdam Powell
06/08/2020, 2:45 PMflow.retryWhen {}
block for handling errors in other streamsAdam Powell
06/08/2020, 2:50 PMEvent
wrapper at best starts to hit a local maximum, it works when you assume conflated state endpoints and events that can be safely conflated in such a way without disrupting the end resultAdam Powell
06/08/2020, 2:52 PMAdam Powell
06/08/2020, 2:53 PMAdam Powell
06/08/2020, 2:55 PMAdam Powell
06/08/2020, 2:56 PMAdam Powell
06/08/2020, 2:57 PMmanueldidonna
06/08/2020, 4:26 PMBaseScene
is just an abstraction over the activity lifecycle which has start/stop and attach/detach callbacksmanueldidonna
06/08/2020, 4:33 PMBaseScene
survives to configuration changes and what I've called FakeViewImpl in the example should be a real inflated android viewTolriq
06/09/2020, 12:40 PM