问题背景
我们在kotlin的学习和使用过程中,协程是一个很重要的概念,这个也是java没有的,那么,什么是协程呢? 协程的定义:可以将它理解成一种轻量级的线程。我们之前所学习的线程是非常重量级的,它需要依靠操作系统的调度才能实现不同线程之间的切换。使用协程就可以在编程语言的层面实现不同协程之间的切换,大大提升并发编程的运行效率。
问题分析
大概了解了协程的概念之后,第二个问题,协程是怎么使用的呢? 1、导入依赖库 想要使用协程功能,需要先在app/build.gradle文件当中添加如下依赖库:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"
2、简单启动一个协程
fun main() {
GlobalScope.launch {
println("Coroutines in Thread.currentThread().name" + Thread.currentThread().name)
}
}
运行结果如下: Global.launch函数每次创建的都是一个顶层协程,这种协程当应用程序运行结束时也会跟着一起结束。刚才的代码所以无法打印出来,就是因为代码块中的代码还没来得及运行,应用程序就结束了。 现在我们让程序延迟一段时间再结束,代码如下所示:
fun main() {
GlobalScope.launch {
println("Coroutines in Thread.currentThread().name" + Thread.currentThread().name)
}
Thread.sleep(1000)
}
运行结果如下: 这种写法还是存在问题的,如果代码块中的代码在1秒钟之内不能运行结束,那么就会被强制中断。比如下面代码:
fun main() {
GlobalScope.launch {
println("Coroutines in Thread.currentThread().name begin" + Thread.currentThread().name)
delay(1500)
println("Coroutines in Thread.currentThread().name" + Thread.currentThread().name)
}
Thread.sleep(1000)
}
运行结果如下: 协程中代码执行到一半就被强行中止了,这个问题怎么处理呢? 借助runBlocking函数就可以实现这个功能,代码如下:
fun main() {
runBlocking {
println("Coroutines in Thread.currentThread().name begin" + Thread.currentThread().name)
delay(1500)
println("Coroutines in Thread.currentThread().name" + Thread.currentThread().name)
}
Thread.sleep(1000)
}
运行结果如下: 3、创建多个协程 使用launch函数,代码如下:
fun main() {
runBlocking {
launch {
println("launch1")
delay(1000)
println("launch1 finished")
}
launch {
println("launch2")
delay(1000)
println("launch2 finished")
}
}
}
运行结果如下: 可以看到,两个子协程中的日志是交替打印的,说明它们确实是像多线程那样并发运行的。然而这两个子协程实际却运行在同一个线程当中,只是由编程语言来决定如何在多个协程之间进行调度,不需要操作系统参与,这也就使得协程的并发效率会高得多。 不过,随着launch函数中的逻辑越来越复杂,可能你需要将部分代码提取到一个单独的函数中。为此Kotlin提供了一个suspend关键字,使用它可以将任意函数声明成挂起函数,而挂起函数之间都是可以互相调用的,如下所示:
suspend fun printHello(){
println("hello")
delay(1000)
}
suspend函数只能在协程中或者另外一个suspend函数中调用,调用suspend函数时,协程会挂起,即从当前的线程脱离,等这个函数执行完毕再切回原线程继续执行。 4、协程取消与超时 一个页面启动了一个协程,当页面关掉时我们需要取消协程,要不然等协程切回主线程更新UI时就会出错,还有一个场景就是我们在做网络请求时,通常需要设置一个超时时间。下面是协程取消的例子,代码如下:
val job = Job()
val scope = CoroutineScope(job)
scope.launch {
//处理具体逻辑
}
job.cancel()
下面是协程超时的例子,代码如下:
GlobalScope.launch(Dispatchers.Main) {
var user = doIO()
Log.v("Tag", "result $user")
}
suspend fun doIO(): String? {
var res = withContext(Dispatchers.IO) {
withTimeoutOrNull(4000) {
delay(3000)
"done"
}
}
return res
}
上面是一个超时的例子,withTimeoutOrNull()函数表示,如果在指定的时间内完成了工作,就返回下面的结果("done"),如果未能完成的话会返回null。 5、async和await 协程在切线程后或者调用suspend函数后,虽然没有阻塞协程所在线程,但协程是被阻塞的(挂起),例如在一个协程内需要做两个不相关的请求,他们执行起来也是串行的,使用async函数即可并行的执行:async函数必须在协程作用域当中才能调用,它会创建一个新的子协程并返回一个Deferred对象,如果我们想要获取async函数代码块的执行结果,只需要调用Deferred对象的await()方法即可,代码如下所示:
fun main() {
runBlocking {
val result = async {
1 + 1
}.await()
println(result)
}
}
运行结果如下:
问题总结
本文主要介绍了部分kotlin中协程的相关概念,包括什么是协程、如果创建一个协程、创建多个协程、协程的取消和定时、以及async等关键字的介绍,有兴趣的同学可以进一步深入研究。