目录
kotlin之协程(一),线程,进程,协程,协程可以替换线程吗?
kotlin之协程(二),Kotlin协程是什么、挂起是什么、挂起的非阻塞式
kotlin之协程(三),开始创建协程,launch,withContext
kotlin之协程(四),协程的核心关键字suspend
kotlin之协程(五),launch 函数以及协程的取消与超时
kotlin之协程(六),协程中的 async和launch的区别以及runBlocking
kotlin之协程(七),协程中relay、yield 区别
前言
在开始做安卓之前都是学习如何用框架,用一个库,但是在用的过程中不了解核心本质,无法理解其含义.
后来就换了学习的方法
在用一个东西之前,都是先了解这个事物的本质,再去学习如何用,这样的形式可能在用的过程中有更深的体会.在使用的过程中再去更深入的了解其如何实现.
我们想在kotlin使用协程
项目中配置对 Kotlin 协程的支持
在使用协程之前,我们需要在 build.gradle 文件中增加对 Kotlin 协程的依赖:
- 项目根目录下的 build.gradle :
buildscript {
ext.kotlin_coroutines = '1.4.0'
}
- Module 下的 build.gradle
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
}
创建协程
kotlin 中 GlobalScope 类提供了几个携程构造函数:
- launch - 创建协程
- async - 创建带返回值的协程,返回的是 Deferred 类
- withContext - 不创建新的协程,指定协程上运行代码块
- runBlocking - 不是 GlobalScope 的 API,可以独立使用,区别是 runBlocking 里面的 delay 会阻塞线程,而 launch 创建的不会
先跑起来一个简单的例子:
import kotlinx.coroutines.*
fun main() {
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
println("World!") // 在延迟后打印输出
}
println("Hello,") // 协程已在等待时主线程还在继续
Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}
协程最简单的使用方法,其实在前面章节就已经看到了。我们可以通过一个 launch 函数实现线程切换的功能
coroutineScope.launch(Dispatchers.IO) {
...
}
所以,什么时候用协程?当你需要切线程或者指定线程的时候。你要在后台执行任务?切!
launch(Dispatchers.IO) {
val image = getImage(imageId)
}
然后需要在前台更新界面?再切!
coroutineScope.launch(Dispatchers.IO) {
val image = getImage(imageId)
launch(Dispatchers.Main) {
avatarIv.setImageBitmap(image)
}
}
好像有点不对劲?这不还是有嵌套嘛。
如果只是使用 launch 函数,协程并不能比线程做更多的事。不过协程中却有一个很实用的函数:withContext 。这个函数可以切换到指定的线程,并在闭包内的逻辑执行结束之后,自动把线程切回去继续执行。那么可以将上面的代码写成这样:
coroutineScope.launch(Dispatchers.Main) { // 在 UI 线程开始
val image = withContext(Dispatchers.IO) { // 切换到 IO 线程,并在执行完成后切回 UI 线程
getImage(imageId) // 将会运行在 IO 线程
}
avatarIv.setImageBitmap(image) // 回到 UI 线程更新 UI
}
我们甚至可以把 withContext 放进一个单独的函数里面:
launch(Dispatchers.Main) { // 在 UI 线程开始
val image = getImage(imageId)
avatarIv.setImageBitmap(image) // 执行结束后,自动切换回 UI 线程
}
//
fun getImage(imageId: Int) = withContext(Dispatchers.IO) {
...
}
这就是之前说的「用同步的方式写异步的代码」了。
不过如果只是这样写,编译器是会报错的:
fun getImage(imageId: Int) = withContext(Dispatchers.IO) {
// IDE 报错 Suspend function'withContext' should be called only from a coroutine or another suspend funcion
}
意思是说,withContext 是一个 suspend 函数,它需要在协程或者是另一个 suspend 函数中调用。
what?
suspend函数我们在kotlin之协程(二),Kotlin协程是什么、挂起是什么、挂起的非阻塞式有提到,下一章我们专门讲解kotlin协程中的suspend