Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

📚 If you are a beginner to Coroutines on the Android platform, check out the previous article: The Android Coroutines Series: Getting Started

🍈 CoroutineStart profile

The globalScope. launch constructor needs to pass in the context, start, block, and coroutine startup mode. If not, the DEFAULT is CoroutineStart. DEFAULT, launch () the source code is as follows:

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope. () - >Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}
Copy the code

In Kotlin coroutines, the CoroutineStart startup mode is an enumeration:

public enum class CoroutineStart {
    DEFAULT,
    LAZY,
    @ExperimentalCoroutinesApi
    ATOMIC,
    @ExperimentalCoroutinesApi
    UNDISPATCHED;
}
Copy the code
model function
DEFAULT The system enters the pending state immediately
LAZY Scheduling starts only when (start/join/await) is needed
ATOMIC Similar to DEFAULT, and cannot be cancelled before the first suspension point
UNDISPATCHED Execute the coroutine body in the current thread immediately until the first hang start is encountered (later depends on the scheduler)

🍈 DEFAULT

The most common of the four boot modes are DEFAULT and LAZY.

DEFAULT is a hanhan-type startup. After launch is invoked, it will enter the state to be scheduled immediately and can be executed once the scheduler is OK. Example:

suspend fun main(a) {
    println(1)

    val job = GlobalScope.launch {
        println(2)
    }

    println(3)

    job.join()// Wait for the coroutine to finish executing

    println(4)}Copy the code

Print the result

1
3
2
4
Copy the code

The code above uses the default startup mode and does not specify a scheduler, so the scheduler is default. The default implementation of the scheduler on the JVM is to open a thread pool.

But just a few threads are enough to schedule thousands of coroutines, and each coroutine has its own call stack, which is fundamentally different from simply opening a thread pool to perform asynchronous tasks.

🍈 LAZY

LAZY is a LAZY start. There is no scheduling behavior after launch, and the coroutine body will not be executed until we need it to be.

  • callJob.startAnd actively trigger the scheduling execution of coroutines
  • callJob.joinImplicitly triggers the scheduled execution of the coroutine
  • callasync.await()
suspend fun main(a) {
    println(1)

    val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
        println(2)
    }

    println(3)

    job.start()// The coroutine starts

    println(4)

    Sleep only keeps the process alive in order to wait for the coroutine to complete
    Thread.sleep(5000L)}Copy the code

Print the result

1
3
4
2
Copy the code

🍈 ATOMIC

ATOMIC is similar to DEFAULT and cannot be cancelled before the first mount

ATOMIC only makes sense when referring to cancels (coroutines can be cancelled, but there are certain conditions). Therefore, the results will be different depending on the time when cancel is called, for example, before coroutine scheduling, when scheduling is started but not executed, when execution is started, when execution is completed, and so on.

🔺 Note: All suspend functions under the Kotlinx. coroutines package are cancelable. These suspend functions check for the cancellation status of the coroutine and throw a CancellationException when canceled

@ExperimentalCoroutinesApi
suspend fun main(a) {
    println(1)

    val job = GlobalScope.launch(start = CoroutineStart.ATOMIC) {
        println("Before suspend")
        delay(2000)
        println(2)
    }

    println(3)

    job.cancel()// Coroutine cancelled

    println(4)}Copy the code

Print the result

1
3
suspendHang up before4
Copy the code

In the case of ATOMIC mode, we’ve already discussed that it must be started. In fact, it does not stop until it reaches its first suspend point. Delay is a suspend function, at which point our coroutine reaches its first suspend point. So the following 2 will not be printed.

🍈 UNDISPATCHED

Coroutines in this mode start executing directly on the current thread until the first hang start. This sounds a bit like the previous ATOMIC except that UNDISPATCHED starts executing the coroutine body without going through any dispatcher. Of course, execution after a hang point is encountered depends on the logic of the hang point itself and the scheduler in the context.

@ExperimentalCoroutinesApi
suspend fun main(a) {
    println(1)

    val job = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
        println("Before suspend")
        delay(2000)
        println(2)
    }

    println(3)

    job.join()

    println(4)}Copy the code

Print the result

1
suspendHang up before3

2
4
Copy the code

The coroutine is executed in the current thread immediately after it is started. Therefore, 1. “Suspend before” is executed in the same thread (main thread), delay is the starting point of suspension, and 2 is scheduled again after 2000ms. Therefore, perform 4 after 2 is displayed.