-
Notifications
You must be signed in to change notification settings - Fork 510
Description
@gpeal @elihart @sav007
In Lifecycle 2.6.0 the functions whenXXX/launchWhenXXX were deprecated, see explanation here
Overall they outline four options for the work to behave when the lifecycle fall below "X" state:
- The work is paused (current, but deprecated behavior of
whenStarted) - Let the suspending code continue to run to completion (even if state falls below target state)
- Cancel the suspending code and restart it when you come back above that state.
- Cancel the suspending code and don't restart it
Initially, maverick's viewModel.onEach behaved as 2 - the callback was guaranteed to be triggered when lifecycle state is above state, but execution wasn't paused/cancelled if the state falls below started state.
e.g. the following was possible:
viewModel.onEach(State::property) { property ->
binding.textView.text = "A" // guaranteed state above Started
delay(1000)
binding.textView.text = "B" // no guarantee
In #665 the behavior was changed to 1, as the callback is wrapped into whenStarted
As it is deprecated now (in #689 deprecations were suppressed), eventually it might be removed/hidden so we need to figure out what would be the best expected behavior for mavericks.
Here are my thoughts
- Behavior 3 & 4 do not make much sense as on higher level there is a
DeliveryMode(UniqueOnly,RedeliverOnStart) which allow to configure similar behavior -RedeliveryOnStart=Behavior 3,UniqueOnly=Behavior 4 - Behavior 1 (current, with
whenStarted) only makes sense withUniqueOnly, as withRedeliverOnStartprevious work will not be resumed (so it will be equivalent to Behavior 4). It will be cancelled because of new 'redeliver' emission - Behavior 2 (behavior prior 3.0.2) actually still makes sense. I was thinking some more about the example in Wrap subscription action with lifecycle
whenStarted#665
override fun onCreate(savedInstanceState: Bundle?) {
viewModel.onEach(ViewState::isVisible) { isVisible ->
...
binding.someview.reveal() // (1) suspendable, performs animation
binding.someview.text = "" // (2) touches other UI after continuation
...
}
}
Despite it fixes crash (if view's lifecycle fall into DESTROYED state, while fragment's lifecycle is still in CREATED), it introduce some potential issues.
-
Issue 1 - the code won't be ever "resumed" from suspended state just because of
RedeliverOnStart, sobinding.someview.text = ""will never be called -
Issue 2 - even if it would resume (e.g. if
UniqueOnlydelivery mode was used),binding.someview.text = ""might reference to another view (if view was recreated).
I think the proper solution/advice would be to move viewModel.onEach that does "suspending stuff with a view" to Fragment.onViewCreated. In this case the job will be created in view's lifecycle scope, so the animation will be tied to view rather than fragment which is more correct