全民 Kotlin:协程特别篇_Android技术之家的博客
什么是协程
先来看官方对 Kotlin 协程的介绍:Coroutines basics
A coroutine is an instance of suspendable computation. It is conceptually similar to a thread, in the sense that it takes a block of code to run that works concurrently with the rest of the code. However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one.
Coroutines can be thought of as light-weight threads, but there is a number of important differences that make their real-life usage very different from threads.
翻译成中文的意思是:
一个协同程序是悬浮计算的一个实例。它在概念上类似于线程,从某种意义上说,它需要一个与其余代码同时运行的代码块来运行。但是,协程不受任何特定线程的约束。它可以在一个线程中暂停其执行并在另一个线程中恢复。
协程可以被认为是轻量级线程,但是有许多重要的区别使得它们在现实生活中的使用与线程大不相同。
在我看来,协程是 Kotlin 对线程和 Handler 的 API 的一种封装,是一种优雅处理异步任务的解决方案,协程可以在不同的线程来回切换,这样就可以让代码通过编写的顺序来执行,并且不会阻塞当前线程,省去了在各种耗时操作写回调的情况。
suspend 关键字介绍
在正式开讲协程之前,先介绍一下 Kotlin 语法的关键字:suspend,suspend 的中文意思是挂起,可以理解为把当前线程暂时挂起,稍后自动切回来到原来的线程上,可用于修饰普通的方法,表示这个方法是一个耗时操作,只能在协程的环境下才能调用,又或者在另一个 suspend 方法中调用。
当代码执行到有 suspend 关键字修饰的方法上,会先挂起当前线程的执行,需要注意的是这里的挂起是非阻塞式的(也就是不会阻塞当前线程情况下),然后就会去先执行带有 suspend 修饰的方法上,当这个方法执行完成后,会让刚刚挂起的线程继续往下执行,这样我们看到的代码顺序就是代码执行的顺序。
另外需要注意的是 suspend 本身不会起到一种线程挂起或者线程切换的效果,那么它真正的作用是什么呢?其实它更多的是一种提醒,表示这是一个耗时方法,不能直接执行,需要把我放到协程中去调用,所以我们在写某个耗时方法的时候需要给它加上 suspend 关键字,这样可以有效避免我们在主线程中调用耗时操作造成应用卡顿的情况。
suspend fun getUserName(userId: Int): String {
delay(20000)
return "Android 轮子哥"
}
集成协程
dependencies {
// Kotlin 协程:https://github.com/Kotlin/kotlinx.coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
}
协程常见的三个操作符号
runBlocking:中文意思是运行阻塞,顾名思义,会阻塞当前线程执行
launch:中文意思是启动,不会阻塞当前线程,但是会异步执行代码
async:中文意思是异步,跟 launch 相似,唯一不同的是它可以有返回值
runBlocking 用法
runBlocking 的中文翻译:运行阻塞。说太多没用,直接用代码测试一下
println("测试开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
runBlocking {
println("测试延迟开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
delay(20000)
println("测试延迟结束")
}
println("测试结束")
12:53:15.799 System.out: 测试开始 true
12:53:15.811 System.out: 测试延迟开始 true
12:53:35.814 System.out: 测试延迟结束
12:53:35.815 System.out: 测试结束
runBlocking 运行在主线程,由此可见和它的名称一样,真的会阻塞当前的线程,只有等 runBlocking 里面的代码执行完了才会执行 runBlocking 外面的代码
到这里大家可能有一个疑问,既然阻塞线程,那我直接用代码写不是也一样?干嘛还用 runBlocking 呢?解决这一疑问很简单,我们只需要找一个没有协程的地方,写一句 delay(20000) 就收到编译器给你的提示:
Suspend function 'delay' should be called only from a coroutine or another suspend function
挂起函数 'delay' 应该只从协程或另一个挂起函数调用
如果我们调用的是被 suspend 修饰的方法,那么它必须要在协程内才能被调用,所以 runBlocking 并非一无是处
launch 用法
launch 的中文翻译:启动,上代码测试
测试的时候是主线程,但是到了 launch 中就会变成子线程,这种效果类似 new Thread(),有木有?和 runBlocking 最不同的是 launch 没有执行顺序这个概念
参考