Article Directory
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.gradle
For 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
}
runBlocking
As 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 , delay
the function is a suspending function, similar to a function in Java sleep
(note: not essentially the same thing). When runBlocking
blocked, the entire thread will be blocked. Because of this, runBlocking
it will not be used for Android business development.
runBlocking {
println("runBlocking!")
delay(1000)
launch {
// 创建一个子协程
println("launch!")
}
}
Output result:
runBlocking!
// 阻塞一秒
launch!
runBlocking
Using the suspend function will also block the sub-coroutine, and will wait until runBlocking
all the internal sub-coroutines have finished running before performing the next step
2.2 GlobalScope
GlobalScope
Example usage of :
@OptIn(DelicateCoroutinesApi::class) // 注解压制
fun main(){
GlobalScope.launch {
// Empty Code
}
}
GlobalScope
The difference with and runBlocking
is 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 GlobalScope
that the thread will not be blocked.
Although GlobalScope
it 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 GlobalScope
the life cycle, you can write an Android Demo to test it. First, create two Activity
: MainActivity
and TestActivity
, from MainActivity
jump to TestActivity
, at TestActivity
startup GlobalScope
.
TestActivity
The 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 GlobalScope
that the coroutine is still executing! If you change it to lifecycleScope
, coroutines will be disabled.
So for GlobalScope
whether it can cancel
be 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)
}
GlobalScope
is coroutineContext
one 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 GlobalScope
the created coroutine to stop running inexplicably ! ( GlobalScope
The coroutine is global), so it needs to be treated with care GlobalScope
.
2.3 CoroutineScope
CoroutineScope
It 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 cancel
close the coroutine through the context parameter, and it CoroutineScope
will GlobalScope
not block the thread as well.
CoroutineScope
Create example:
CoroutineScope(Dispatchers.IO).launch {
// Empty Code
}
cancel
End 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 CoroutineScope
to create coroutines.