协程使用指南
协程介绍
协程是一种并发设计模式,是一套基于线程来实现的API,可以看作是轻量级线程。
协程的一个好处是,当涉及到开发人员时,编写非阻塞代码与编写阻塞代码基本相同。编程模型本身并没有真正改变。
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1'
// 可选 Android中使用协程
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
协程的用法
创建一个协程,并执行
import kotlinx.coroutines.*
fun main() {
// 在后台启动一个新的协程并执行
GlobalScope.launch {
// 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
delay(1000L)
// 在延迟后打印输出
println("World!")
}
// 协程已在等待时主线程还在继续
println("Hello,")
// 阻塞主线程 2 秒钟来保证 JVM 存活
Thread.sleep(2000L)
}
代码运行的结果:
我们使用GlobalScope.launch创建一个协程,并执行它,后面大括号里包着的是协程要执行的内容,类似于线程中的run方法体。
- delay方法只能在协程内部使用,它用于挂起协程,但是不会阻塞当前协程所在的线程。
- Thread.sleep会阻塞当前线程,也就是说当前线程被阻塞在那暂时不能做别的事情了
关于协程的方法只能在协程体里面执行,所以为了方便测试协程代码,我准备使用runBlocking把main方法包起来。
/*
*显式指定了其返回类型 Unit,
* 因为在 Kotlin 中 main 函数必须返回 Unit 类型。
*/
fun main()= runBlocking<Unit> {
// 打印当前线程名称
println(Thread.currentThread().name)
}
执行结果:
runBlocking方法运行一个新的协程并且阻塞当前线程,上面的代码中包裹的是main方法,所以阻塞的是main线程
协程挂起
我们用suspend修饰符来标记函数
挂起函数只被允许在协程或另一个挂起函数中调用
我们先看一个先获取token,再登录的例子,里面用到挂起函数
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
/*
* main函数
*/
fun main()= runBlocking<Unit> {
var time = measureTimeMillis{
val token = getToken()
val loginInfo = login(token)
println(loginInfo)
}
println("Completed in $time ms")
}
/**
* 模拟接口获取token
*/
suspend fun getToken():String{
delay(1000L)
return "woshitoken"
}
/**
* 模拟接口登录
*/
suspend fun login(token:String):String {
delay(1000L)
return "my token is: $token , login success"
}
执行结果:
my token is: woshitoken , login success
Completed in 2012 ms
- 我对挂起的理解:
在上面的代码中有两个挂起函数getToken和login,当程序逻辑走到挂起函数getToken()那里,当前协程被挂起,getToken()被抛出去执行,你可以指定在哪一个线程池中执行,当getToken在外部执行完后再恢复刚才挂起的协程,协程代码继续执行。
async 并发
在概念上,async 就类似于 launch。它启动了一个单独的协程,这是一个轻量级的线程并与其它所有的协程一起并发的工作。不同之处在于 launch 返回一个 Job 并且不附带任何结果值,而 async 返回一个 Deferred —— 一个轻量级的非阻塞 future, 这代表了一个将会在稍后提供结果的 promise。你可以使用 .await() 在一个延期的值上得到它的最终结果, 但是 Deferred 也是一个 Job,所以如果需要的话,你可以取消它。转自 kotlincn.net
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
suspend fun doSomethingUsefulOne(): Int {
// 假设我们在这里做了些有用的事
delay(1000L)
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
// 假设我们在这里也做了些有用的事
delay(1000L)
return 29
}
运行结果:
The answer is 42
Completed in 1055 ms
两个协程是并发执行。
惰性启动的-async
Flow
参考链接
官方协程指南
kaixue
如何正确的在 Android 上使用协程 ?
协程博客