J.Dragon
J.Dragon

Reputation: 715

What is different Job as a argument at CoroutineScope and launch?

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

Answers (2)

Marko Topolnik
Marko Topolnik

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

William Reed
William Reed

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

Related Questions