协程是什么
kotlin语言的一种特性,与线程类似,但是比线程轻量级,不需要系统的调度就可以仅在编译语言上进行切换。
如下
fun foo(){
b()
c()
a()
}
fun bar(){
x()
y()
z()
}
如果不用协程,那么先后调用foo和bar,执行顺序为bcaxyz,如果使用了协程,在协程A中调用foo,在协程B中调用bar,那么他们虽然在一个线程里面,但是彼此都有可能随时因对方后挂起。
协程的基本用法
添加依赖
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
使用协程最简单的方式——Global. launch
class CoroutinesTest {
fun main(){
GlobalScope.launch {
println("codes run in coroutine scope")
}
}
}
这里定义了一个main函数,其中创建了一个协程,在里头打印了一句话。
此时运行程序会发现没有任何输出。
这是因为,Global.launch每次创建的都是一个顶层协程,这种协程当应用程序运行结束也会跟着一起结束,因此无法打印就是因为代码块中的代码还没来得及运行。
解决这个问题,只需要延迟一段时间即可。
ThreadSleep
class CoroutinesTest {
fun main(){
GlobalScope.launch {
println("codes run in coroutine scope")
}
Thread.sleep(1000)
}
}
运用Thread.sleep让主线程阻塞了1秒。
但是这种写法还是存在问题,如果1秒内不能运行完成就会被强制中断。
delay
观察如下代码
fun main(){
GlobalScope.launch {
println("codes run in coroutine scope")
delay(1500)
println("codes run in coroutine finished")
}
Thread.sleep(1000)
}
delay函数可以让当前协程延迟到指定时间结束后在运行。但是他与slepp不同,它是一种非阻塞的挂起函数,他只会挂起当前协程,不会影响其他协程运行,而Threadsleep会阻塞其他协程。
runBlocking
上面的delay虽然可以实现延迟到指定时间,但是如果sleep的时间比延迟指定的时间要短,那么delay后面的代码块会来不及运行。
借助runBlocking可以实现全部代码块实现完后再结束。
fun main(){
runBlocking {
println("codes run in coroutine scope")
delay(1500)
println("codes run in coroutine scope finished")
}
}
runBlocking会保证在代码块完成之前一直阻塞当前线程。要注意的是:runBlock应该只用与测试,在正式环境中容易尝试一些性能上的问题。
创建多个协程——launch
观察如下代码:
fun main(){
runBlocking {
launch {
println("launch1")
delay(1000)
println("launch1 finished")
}
launch {
println("launch2")
delay(1000)
println("launch2 finished")
}
}}
launch与GlobalLaunch不同,首先它必须在协程的作用域中才能调用,其次它会在协程中创建子线程。子协程的特点是如果外层协程结束了,改作用域下的所有子协程会一同结束。
suspend
suspend可以将任意函数声明成挂起函数,而挂起函数之间可以相互调用。
suspend fun printDot(){
println(".")
delay(1000)
}
但是你却无法使用launch,因为launch需要在协程的作用域中才能调用。
coroutineScope
suspend fun printDot() = coroutineScope{
launch {
println('.')
delay(1000)
}
}
coroutineScope会继承外部的协程的作用域创建一个子协程,因此在printDot也可以调用launch了。
同样的,coroutineScope跟runBlock有点类似,它可以保证其作用域内的所有代码和子协程在全部执行完之前,外部的协程会一直被挂着。
如下代码:
fun main(){
runBlocking {
coroutineScope {
launch {
for (i in 1..10){
println(i)
delay(1000)
}
}
} println("coroutineScope finished")
}
println("runBlocking finished")
}
但是他们的区别是:coroutineScope只会阻塞当前的协程,既不影响线程,也不行其他协程,而runblock会挂起外部线程。