接下来的 kotlin
学习的会以我学习 android
所遇到不会的进行学习。今天学习协程。还有扔物线的教程Kotlin 的协程用力瞥一眼 - 学不会协程?很可能因为你看过的教程都是错的。
1. 准备工作
这里我只说在 gradle
中的配置,其他见:kotlin 中文教程(协程地址)
dependencies {
...
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
}
这个包在 jcenter()
仓库中。
2. 简单的示例
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
fun main(args: Array<String>) {
println("Start")
// 启动第一个协程
GlobalScope.launch {
delay(2000)
println("Hello")
}
// 启动第二个协程
GlobalScope.launch {
delay(1000)
println("World")
}
Thread.sleep(3000) // 等待 3 秒钟
println("Stop")
}
// 输出的结果为:
// Start
// World
// Hello
// Stop
协程的开启方式就是 launch
。其中 delay
就是延迟的意思,传入的参数是毫秒。
我们使用了类似 Thread.sleep() 的 delay() 函数,但是它更优异:它 不会阻塞一个线程 ,但是会挂起协程自身。 当这个协程处于等待状态时该线程会返回线程池中,当等待结束的时候,这个协程会在线程池中的空闲线程上恢复。
3. suspend 修饰的函数
这样的函数只能在协程中执行,上面的 delay
函数也是 suspend
修饰的。同时 suspend
也是一个协程。
fun main(args: Array<String>) {
println("Start")
GlobalScope.launch {
delay(1000)
println("World")
println(testAsyc())
}
Thread.sleep(3000) // 等待 3 秒钟
println("Stop")
}
suspend fun testAsyc(): Int {
delay(1000)
return 54
}
其中 async
跟 lauch
差不多,但是也有区别, async
会返回一个 Deferred<T>
实例,而 lauch
不是。 Deferred<T>
实例打印如下:
val sha = GlobalScope.async {
delay(1000)
println("wujingyue")
}
println(sha) // 打印: DeferredCoroutine{Completed}@4d76f3f8
虽然使用 sha.await()
来得到返回结果,但 await
函数是一个 suspend
函数,这种函数只能在协程中执行,我这里的主线程并没有 suspend
修饰,固然不能正常调用。
fun main(args: Array<String>) {
println("Start")
val sha = GlobalScope.async {
delay(1000)
println("wujingyue")
}
GlobalScope.launch {
println(sha.await())//输出为:kotlin.Unit
}
Thread.sleep(3000) // 等待 3 秒钟
println("Stop")
}
还有一个 runBlocking
也可以创建协程:
fun main(args: Array<String>) {
println("Start")
runBlocking {
val deferred = (1..1_000_000).map { n ->
GlobalScope.async {
delay(3000)
n
}
}
val sum = deferred.sumOf { it.await().toLong() }
println("Sum: $sum")
}
Thread.sleep(3000) // 等待 3 秒钟
println("Stop")
}
suspend fun testAsyc(): Int {
delay(1000)
return 54
}
// 输出:
// Start
// Sum: 500000500000
// Stop
在这个例子中我刻意这样 GlobalScope.async { delay(3000) n }
,我这样的目的是看看先执行 Stop
还是输出 Sum: 500000500000
,结果可以看出先输出 Sum: 500000500000
,这就说明了 runBlocking
是阻塞的,并不是挂起,所谓挂起是指遇到以后先不执行,以后再执行,至于以后什么时候执行我现在暂时还不知道。只不过现在就看看:
fun main(args: Array<String>) {
println("Start")
GlobalScope.launch {
delay(1000)
println("第一次")
}
GlobalScope.launch {
delay(1000)
println("第二次")
}
GlobalScope.launch {
delay(1000)
println("第三次")
}
Thread.sleep(3000) // 等待 3 秒钟
println("Stop")
}
// 输出:
// Start
// 第二次
// 第一次
// 第三次
经过多次输出,发现并不是固定的,所以挂起以后什么时候执行是未知的。
可能我们还有这样的需求,就是所有的异步中,必须等待其中一个异步完成才能做下面的工作,那就这样。
suspend fun main(args: Array<String>) {
println("Start")
GlobalScope.launch {
println("第一次")
}
val n = GlobalScope.launch {
delay(10000)
println("第N次")
}
n.join()
println("Stop")
}
其中的 n.join()
就是了,意思就是等 n
这个协程完成以后再干活。我就比较喜欢等后台写完接口再干活,因为写界面非常简单,有数据了才写感觉很爽,不然的话自己造的数据最后还要根据后台变动而变动,所以一般两种情况我会进入开发,第一后台接口好了, UI
好了,第二就是数据格式定义好了, UI
好了;如果 UI
很复杂的话,我可以先把底子打好然后再写。
网友评论