什么是协程
协程:是一种更为灵活高效的用户线程,能够选择异步还是同步执行,指定运行的线程。
异步,同步编程:是指的协程能够选择自身的启动模式,在当前线程阻塞式运行,还是在后台异步执行;
指定运行线程:能够方便的选择执行的线程是后台线程还是UI主线程
进程,线程,协程关系
进程>线程>协程,即一个进程可以包含多个线程,一个线程上面可以运行多个协程。
启动方式
目前从非协程环境中启动协程环境主要有三种方式:
- runBlocking :创建新的协程,运行在当前线程上,所以会阻塞当前线程,直到协程体以及所有子协程结束
- GlobalScope.lanuch:启动一个新的线程,在新线程上创建运行协程,不阻塞当前线程
- GlobalScope.asyn:启动一个新的线程,在新线程上运行协程,并且不阻塞当前线程,支持await获取返回值
- withContext:可直接返回耗时任务的结果。一般来说,多个withContext任务是串行的。withContext可用来在协程中控制和切换部分任务执行所在的线程,接收的参数也是协程调度器,由此控制切换任务所在线程.
suspend关键字
kotlin中的一个关键字,它一般标识在一个函数的开头,用于表示该函数是个耗时操作,这个关键字主要作用就是为了作一个提醒,并不会因为添加了这个关键字该函数就会立即跑到子线程。suspend函数是只能再协程体内生效,在Kotlin协程中,当遇到supsend函数的时候,该协程会自动逃离当前所在的线程执行任务,此时原来协程所在的线程就继续干自己的事情,等到协程的suspend函数执行完成后又自动切回原来的线程继续往下走。
添加依赖
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'
runBlocking
创建新的协程运行在当前线程上,所以会阻塞当前线程,直到协程体结束
适用范围:
用于启动一个协程任务,通常只用于启动最外层的协程,例如:线程环境切换到协程环境
示列:
fun main(args: Array<String>) = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking:"+Thread.currentThread().name)
}
coroutineScope { // Creates a coroutine scope
launch {
delay(500L)
println("Task from nested launch:"+Thread.currentThread().name)
}
delay(100L)
println("Task from coroutine scope:"+Thread.currentThread().name) // This line will be printed before the nested launch
}
println("Coroutine scope is over:"+Thread.currentThread().name) // This line is not printed until the nested launch completes
}
打印结果如下:
Task from coroutine scope:main
Task from runBlocking:main
Task from nested launch:main
Coroutine scope is over:main
GlobalScope.lanuch
创建新的协程,默认运行在后台新线程,并且不阻塞当前线程
适用范围:
需要启动异步线程处理的情况
示列
fun main(args: Array<String>)= runBlocking {
var job=GlobalScope.launch {//启动一个新的协程
println("GlobalLanuch:"+Thread.currentThread().name)
delay(1000)
println("World!")
}
println("Hello")
println("runBlocking:"+Thread.currentThread().name)
job.join()
}
打印如下:
Hello
runBlocking:main
GlobalLanuch:DefaultDispatcher-worker-1
World!
GlobalScope.async
创建新的协程,默认运行在后台新的线程中,并且不阻塞当前线程,支持通过await获取返回值
适用范围:
特别是需要启动异步线程处理并等待处理结果返回的场景
示列:
suspend fun getToken(): String { //suspend挂起的是协程本身不是本方法
delay(300)
println("getToken 开始执行,时间: ${System.currentTimeMillis()}")
return "ask"
}
suspend fun getResponse(token: String): String {
delay(100)
println("getResponse 开始执行,时间: ${System.currentTimeMillis()}")
return "response"
}
fun setText(response: String) {
println("setText 执行,时间: ${System.currentTimeMillis()}")
}
fun main(args: Array<String>) {
println("协程 开始执行,时间: ${System.currentTimeMillis()}")
runBlocking {
var job=GlobalScope.launch {
//async 同 launch 唯一的区别就是 async 是有返回值的
var token=async {
return@async getToken()
}.await()
var response=async {
return@async getResponse(token)
}.await()
setText(response)
}
job.join()
//job.start() - 启动协程,除了 lazy 模式,协程都不需要手
//动启动
//job.join() - 等待协程执行完毕
//job.cancel() - 取消一个协程
//job.cancelAndJoin() - 等待协程执行完毕然后再取消
}
}
打印如下:
协程 开始执行,时间: 1585214992756
getToken 开始执行,时间: 1585214993133
getResponse 开始执行,时间: 1585214993243
setText 执行,时间: 1585214993243
withContext切换协程所在的线程
companion object{
@JvmStatic
fun main(args: Array<String>) {
switchThread()
}
fun switchThread()= runBlocking {
launch {
println("start in thread ${Thread.currentThread().name}")
val job= withContext(Dispatchers.IO){
delay(5000)
println("I am working in thread ${Thread.currentThread().name}")
}
println("end in thread :${Thread.currentThread().name}")
}
}
}
打印如下:
start in thread main
I am working in thread DefaultDispatcher-worker-1
end in thread :main









网友评论