Reputation: 715
This two code run exactly same. What is different putting Job in CoroutineScope and launch?
private val job = CoroutineScope(Dispatchers.Main).launch(start = CoroutineStart.LAZY) {
for(i in 10 downTo 0) {
Log.d("test", ": $i")
delay(1000)
}
}
CoroutineScope(Dispatchers.Main+job).launch{ }
CoroutineScope(Dispatchers.Main).launch(job) { }
Upvotes: 5
Views: 2365
Reputation: 200296
Technically, both result in the same behavior, but the main point is that neither is a good way to use the CoroutineScope()
factory. This is the idiomatic way to write the same thing:
GlobalScope.launch(Dispatchers.Main+job) { ... }
If this raises your eyebrows ("Don't use GlobalScope!"), that's because it should — your examples are just another way to make the same mistake, with more verbose code. You construct a CoroutineScope
without holding a reference to it, resulting in exactly the same unbounded and non-cancellable scope as the GlobalScope
singleton.
In addition, the way you use a Job
instance that is a handle to an actual coroutine, is also wrong: the job associated with a coroutine scope should be a standalone instance returned from either Job()
or SupervisorJob()
. Its only purpose is to serve as the central point from which to cancel the entire scope or inspect its state.
Upvotes: 6
Reputation: 1844
It doesn't seem like there are many differences between the two, looking at the source code. The operator fun plus
documentation states
Returns a context containing elements from this context and elements from other context. The elements from this context with the same key as in the other one are dropped.
which explains how your first test works. For the second, calling launch
with a context parameter, calls into CoroutineScope.newCoroutineContext
Creates a context for the new coroutine. It installs Dispatchers.Default when no other dispatcher or ContinuationInterceptor is specified, and adds optional support for debugging facilities (when turned on).
and looking at the source code of it:
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
val combined = coroutineContext + context
val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
debug + Dispatchers.Default else debug
}
We can see it also ends up using the operator fun plus
.
There are some other differences that seem negligable to mention here, but ultimately the context of the launched coroutine looks to be the same in both of your tests.
Upvotes: 1