The road to learning Kotlin coroutines (1): Getting to know coroutines for the first time

foreword

Coroutine: English coroutine, coroutine can be considered as a lightweight thread, which is a package based on Java thread pool . Compared with threads that have to deal with various synchronization issues, coroutines can simplify it and write asynchronous code in a synchronous manner .

1. gradle configuration

build.gradleFor Android projects, the latest version of kotlin only needs to add the following support under the project :

// kotlin核心扩展包,包括协程
implementation androidx.core:core-ktx:1.6.0

// 提供了lifecyclerScope
implementation androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1

// 提供了viewModelScope
implementation androidx.lifecycle:lifecycle-runtime-ktx:2.5.1

For standalone Kotlin projects, the following configuration is required:

// kotlin标准库
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.7.20"

// 协程核心库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"

2. Three ways to create a coroutine

2.1 runBlocking

runBlocking {
    
    
	// Empty Code
}

runBlockingAs the name implies, the creation of the coroutine will cause the thread to be blocked.

Thread{
    
    
   runBlocking {
    
    
      println("runBlocking start!")
      delay(2 * 1000)
   }
   println("Thread end!")
}.start()

Output result:

runBlocking start!
// 阻塞一秒
Thread end!

println("Thread end!")To hang for two seconds before executing , delaythe function is a suspending function, similar to a function in Java sleep(note: not essentially the same thing). When runBlockingblocked, the entire thread will be blocked. Because of this, runBlockingit will not be used for Android business development.

runBlocking {
    
    
	println("runBlocking!")
	delay(1000)
	launch {
    
     // 创建一个子协程
		println("launch!")
    }
}

Output result:

runBlocking!
// 阻塞一秒
launch!

runBlockingUsing the suspend function will also block the sub-coroutine, and will wait until runBlockingall the internal sub-coroutines have finished running before performing the next step

2.2 GlobalScope

GlobalScopeExample usage of :

		@OptIn(DelicateCoroutinesApi::class) // 注解压制
        fun main(){
    
    
            GlobalScope.launch {
    
    
				// Empty Code
            }
        }

GlobalScopeThe difference with and runBlockingis that it doesn't block the thread.

		@OptIn(DelicateCoroutinesApi::class)
        fun main(){
    
    
            Thread{
    
    
                GlobalScope.launch {
    
    
                    println("GlobalScope launch!")
                    delay(1000)
                }
                println("Thread end !")
            }.start()
        }

Output result:

Thread end !

It can be seen from the output GlobalScopethat the thread will not be blocked.

Although GlobalScopeit will not block threads, it is also not recommended for Android development ! Because, it is global and will be kept until the application dies.

Regarding GlobalScopethe life cycle, you can write an Android Demo to test it. First, create two Activity: MainActivityand TestActivity, from MainActivityjump to TestActivity, at TestActivitystartup GlobalScope.
TestActivityThe startup code is as follows:

			R.id.start_btn -> {
    
    
                GlobalScope.launch(Dispatchers.IO) {
    
    
                    while (true){
    
    
                        delay(1000)
                        Log.e("test", "GlobalScope!")
                    }
                }
            }

By clicking the Button to start GlobalScope, and then close TestActivity, you will find GlobalScopethat the coroutine is still executing! If you change it to lifecycleScope, coroutines will be disabled.
So for GlobalScopewhether it can cancelbe used to close? In fact, it doesn't work, and GlobalScope.cancel()an exception will be thrown when you try to close with: java.lang.IllegalStateException: Scope cannot be cancelled because it does not have a job: kotlinx.coroutines.GlobalScope@274748c.
Why GlobalScope.cancel()does the call throw an exception? We can find the answer from the code.

// GlobalScope,它是一个继承CoroutineScope的单例
@DelicateCoroutinesApi
public object GlobalScope : CoroutineScope {
    
    
    /**
     * Returns [EmptyCoroutineContext].
     */
    override val coroutineContext: CoroutineContext
        get() = EmptyCoroutineContext
}

// cancle代码
public fun CoroutineScope.cancel(cause: CancellationException? = null) {
    
    
    val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
    job.cancel(cause)
}

GlobalScopeis coroutineContextone EmptyCoroutineContext. When calling cancel, coroutineContext[Job]the value of is null, then an exception must be thrown directly.

Although the coroutine cannot GlobalScope.cancel()be closed in the same way, the coroutine can be closed in the following way

			private var jobInGlobal: Job? = null
			// 省略代码...
			R.id.start_btn -> {
    
    
                jobInGlobal = GlobalScope.launch(Dispatchers.IO) {
    
    
                    while (true){
    
    
                        delay(1000)
                        Log.e("test", "GlobalScope!")
                    }
                }
            }

            R.id.stop_btn -> {
    
    
                jobInGlobal?.cancel()
            }

But if you do this, it may cause GlobalScopethe created coroutine to stop running inexplicably ! ( GlobalScopeThe coroutine is global), so it needs to be treated with care GlobalScope.

2.3 CoroutineScope

CoroutineScopeIt is a recommended method to use. You can manage and control the life cycle of the coroutine through the context parameter, and you can also cancelclose the coroutine through the context parameter, and it CoroutineScopewill GlobalScopenot block the thread as well.
CoroutineScopeCreate example:

CoroutineScope(Dispatchers.IO).launch {
    
    
	// Empty Code
}

cancelEnd the coroutine by method:

			private var job: Job? = null
			// 省略代码...
			R.id.start_btn -> {
    
    
                job = CoroutineScope(Dispatchers.IO).launch {
    
    
                    while (true){
    
    
                        delay(1000)
                        Log.e("test", "CoroutineScope!")
                    }
                }
            }

            R.id.stop_btn -> {
    
    
                job?.cancel()
            }

3. Summary

For the three ways of creating coroutines runBlocking, CoroutineScope, GlobalScope, are used in actual development CoroutineScopeto create coroutines.

Guess you like

Origin blog.csdn.net/RQ997832/article/details/128222584