Some basic question about coroutine in android. Ca...
# coroutines
t
Some basic question about coroutine in android. Can I assume that we should always use viewmodelScope, which associated with lifecycle whenever possible. But in some occasion, we might not have those scope, for eg. in service or receiver. In that case, is it advisable to define job and scope within, or use Globalscope?
Copy code
private val job = SupervisorJob()
private val scope = CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO> + job)
d
Only define scope of you are going to cancel it somewhere. Otherwise use global scope.
a
I often wish we had never released the viewModelScope extension, I think it's frequently overused.
Services always have a natural lifecycle that can be used to drive CoroutineScope creation and cancellation, and this should be preferred over GlobalScope.
Receivers often should be quick and delegate work to something else in the app that already has a well defined scope. Sometimes goAsync comes into play and there's a natural scoping point there.
Repository-like objects that must act asynchronously on their own should generally accept a scope as a constructor parameter, occasionally these constructor parameters have a default value. This is a trivial step when writing these classes that will save you a lot of headaches in testability over using GlobalScope.
Avoid GlobalScope. Nearly every instance of its use should be accompanied by a
TODO
to come back and work out a more appropriate scope once you know more about the problem after initial prototyping. There are exceptions but they are rare.
t
Thanks for the great reply! Do u have any example of the exceptions that really cant avoid but use GlobalScope?
a
It's less about when you can't avoid it and more about when you're using it as a syntax shortcut for something equivalent to strong scoping anyway. Compare:
Copy code
val scope = CoroutineScope(context + Job(context[Job]))
scope.launch { ... }
// Later...
scope.cancel()
Vs.
Copy code
val job = GlobalScope.launch(context) { ... }
// Later...
job.cancel()
The latter is still making use of controlled scoping via the input parent
context
and the returned job from the launch, it's just less fiddly.
If you only ever launch into it once, say to set up an actor of sorts, writing it the first way just to dogmatically avoid GlobalScope isn't really helping.
The important part is following structured concurrency and not performing uncontrolled `fork()`s in implementation details of your code.
t
Thanks @Dominaezzz & @Adam Powell!