Arjan van Wieringen
01/07/2023, 3:34 PMSharedFlow<Notification>
• NotificationService.notify(notification: Notifiy)
can be suspend or not.
◦ If suspend
the caller should provide the scope
◦ If not suspend
the NotificationService
should have a scope in which to launch the emit
The question is. What is better? In my opinion a Notification is a side-effect, so if a ViewModel requires to send a notification somewhere it isn't the responsibility of the ViewModel to make sure this succeeds. It is the responsibility of the NotificationService
. The ViewModel's scope can get cancelled.
Is this the right reasoning?franztesca
01/07/2023, 3:46 PMArjan van Wieringen
01/07/2023, 4:47 PMNick Allen
01/07/2023, 8:20 PMlaunch { emit(value) }
, then you have created an unordered infinite buffer system. The continuations all wait so no events are lost until you run out of memory. Calling launch
means you lose ordering since two calls to launch
can run the lambdas in any order (maybe that's a problem for you, maybe not).
My impression is that emit
is only really useful when it is running in the same coroutine as the work producing the emitted values so that suspending stops any new values from being produced since the producer coroutine has been suspended.
I'd rather make that infinite buffer obvious with MutableSharedFlow(extraBufferCapacity=Int.MAX_INT)
/`tryEmit` or Channel(UNLIMITED)
/ trySend
and channel.consumeAsFlow().shareIn(notificationScope)
. I'd think hard on if you really need EVERY notification to succeed or if you could go with a smaller buffer size and maybe implement a DROP_OLDEST strategy or something.Nick Allen
01/07/2023, 8:26 PMPatrick Steiger
01/07/2023, 10:50 PMsuspend
in every domain object and the ViewModel
(or whatever) makes the actual work
I think this approach also facilitates unit testingPatrick Steiger
01/07/2023, 10:51 PMsuspend
all the way beyond the ViewModel , and no suspend at all on View and ViewModelPatrick Steiger
01/07/2023, 11:01 PMCoroutineScope
because that means you would need to manage its lifecycle manually (e.g call scope.cancel()
or cancelChildren()
eventually when the class is no longer in use and ready to be disposed. You generally want the class bound to some Android lifecycle (LifecycleOwner
) , so you’d probably want to pass in the parent CoroutineScope
context (so you inherit the job and cancellation properties), or some similar approach.
I just find it simpler for custom
Components to be cold, not own a scope, and expose suspend apiPatrick Steiger
01/07/2023, 11:12 PMsuspend fun notify() =
ApplicationScope.async {
actuallyNotifyAndReturnResult()
}.join()
Launch computation coroutine elsewhere, wait result on current coroutineArjan van Wieringen
01/08/2023, 8:58 AM