-
Kotlin 文件和类不存在一对一关系
-
共生体
-
继承
-
修饰符
-
空指针问题
正文
重难点
协程
想了很久,关于协程的内容,在官网上确实有很多内容,基础知识概念,基本使用,以及 流操作,通道,异常处理,并发处理等,不方便在这里展开。具体的参照:[Kotlin中文网](
)
这里具体去学习。本章节,只总结一下近期查阅资料并经本人验证的知识点。
概念
英文 coroutines : /,kəuru:'ti:n/ 意: 协同程序。 简称协程。
Kotlin提出协程概念,是为了简化异步编程,让开发者更容易控制函数的执行流程。
协程和线程的联系和区别
在操作系统OS中,进程是资源分配的最小单位,线程是任务调度的最小单位。而协程则是处在线程内部的**“微线程”,或者说轻量级线程**。 由于线程在OS中是稀缺资源,所有OS平台的线程数量都是有上限的,平时编程我们会用线程池来管理线程数量,熟悉线程池的同学应该知道,线程池管理线程,无论是核心线程还是非核心线程,都不会随意去创建,除非迫不得已。
用线程解决异步问题
- 多线程同步编程可以通过加锁解决数据的线程安全问题,但是加锁会降低程序执行效率,并且锁多了,会有死锁隐患
- 线程的状态转换完全由内核控制,程序员开发者无法干涉
- 线程的是稀缺资源,不能随意创建,使用线程解决异步问题,线程的初始化,上下文切换(CPU轮转),线程状态切换(sleep,yield…), 变量加锁操作(synchronized关键字),都会使得线程的使用代价比较大
用协程解决异步问题
- 协程是运行在线程之上的优化产物,或称“微线程”。协程依赖线程运行,复杂的底层逻辑被封装在库内,使用时无需关心所处线程状态
- 使用协程,开发者可以自己控制协程的状态(suspend挂起,resume恢复),而不会像线程那样依赖底层调度,时间片争夺。
- 一个线程可以跑多个协程,一个协程也可以分段在多个线程上执行
- 协程 是 非阻塞的,当前协程挂起之后,所在线程资源并不会浪费,它会去执行其他协程(如果有的话)
- 协程 相对于线程这种OS中的稀缺资源,它是极其轻量级的,就算你开一百万个协程,对于系统的压力也不会像大量线程那样大(别说一百万个,linux系统的线程数量上线是1000,超过这个值系统就无法正常运行).
- 总之一句话 : 协程的出现,让程序开发者对程序逻辑的掌控提升到了一个新的境界,想象一下,一个函数正在执行,你想让他在某个时刻暂停,然后在另一个时刻继续。利用线程恐怕很难做到。协程中,轻而易举。
基本使用
module级别的build.gradle 中 引入库依赖,推荐使用最新版(目前稳定版是1.3.3)
dependencies {
//...
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3" // 如果你需要用到协程调度器Dispatchers的话,必须加上这个依赖
}
协程的创建
创建协程有多种方式,全局/独立,同步/异步,并且可以指定 “协程调度器”
-
runBlocking
fun main() { runBlocking { println("这是runBlocking协程...") } }
-
launch
fun main() { runBlocking { println("这是runBlocking协程...") launch { println("这是runBlocking内部的runBlocking协程...") delay(2000) println("延迟了2000MS之后,再打印") } } }
-
GlobalScope.launch
fun main() { println("协程,相对于主线程来说,都是异步的,也就是说,你在这里插入协程逻辑,主线程的逻辑并不会被阻塞") GlobalScope.launch { delay(3000) println("GlobalScope.launch 创建协程 ") } println("主线程继续") Thread.sleep(5000) }
-
Global.async
fun main() { runBlocking { val async: Deferred<String> = GlobalScope.async { println("这是一个异步协程,他将返回一个Deferred") delay(2000) "异步任务返回值" } println("主线程继续:" + async.await()) } Thread.sleep(5000) }
“骚操作”
关心协程的人一般都会十分关注它到底能给我们异步编程带来怎样的便利。这里总结几个不用协程实现起来很麻烦的骚操作。
-
如果有一个函数,它的返回值需要等到多个耗时的异步任务都执行完毕返回之后,组合所有任务的返回值作为 最终返回值
fun test6(): String = runBlocking { var finalRes = "" coroutineScope { launch { delay(1000) finalRes = finalRes.plus("1") } launch { delay(2000) finalRes = finalRes.plus("2") } launch { delay(3000) finalRes = finalRes.plus("3") } } finalRes } fun main() { val test6 = test6() println("最终返回值是: $test6") }
最终返回结果为(延迟3秒之后打印):
最终返回值是: 123
-
如果有一个函数,需要顺序执行多个网络请求,并且后一个请求依赖前一个请求的执行结果
import kotlinx.coroutines.* suspend fun getToken(): String { for (i in 0..10) { println("异步请求正在执行:getToken :$i") delay(100) } return "ask" } suspend fun getResponse(token: String): String { for (i in 0..10) { println("异步请求正在执行:getResponse :$token $i") delay(100) } return "response" } fun setText(response: String) { println("setText 执行,时间: ${System.currentTimeMillis()}") } fun main() { GlobalScope.launch(Dispatchers.Unconfined) { var token = GlobalScope.async(Dispatchers.Unconfined) { return@async getToken() }.await() // 创建异步任务,并且 阻塞执行 await 是阻塞执行取得结果 var response = GlobalScope.async(Dispatchers.Unconfined) { return@async getResponse(token) }.await() // 创建异步任务,并且立即执行 setText(response) } Thread.sleep(20000) }
执行结果:
异步请求正在执行:getToken :0 异步请求正在执行:getToken :1 异步请求正在执行:getToken :2 异步请求正在执行:getToken :3 异步请求正在执行:getToken :4 异步请求正在执行:getToken :5 异步请求正在执行:getToken :6 异步请求正在执行:getToken :7 异步请求正在执行:getToken :8 异步请求正在执行:getToken :9 异步请求正在执行:getToken :10 异步请求正在执行:getResponse :ask 0 异步请求正在执行:getResponse :ask 1 异步请求正在执行:getResponse :ask 2 异步请求正在执行:getResponse :ask 3 异步请求正在执行:getResponse :ask 4 异步请求正在执行:getResponse :ask 5 异步请求正在执行:getResponse :ask 6 异步请求正在执行:getResponse :ask 7 异步请求正在执行:getResponse :ask 8 异步请求正在执行:getResponse :ask 9 异步请求正在执行:getResponse :ask 10 setText 执行,时间: 1578904290520
-
当前正在执行一项异步任务,但是你突然不想要它执行了,随时可以取消
fun main() { // 协程任务 val job = GlobalScope.launch(Dispatchers.IO) { for (i in 0..100){// 每次挂起100MS,100次也就是10秒 println("协程正在执行 $i") delay(100) } } // 但是我在1秒之后就取消协程 Thread.sleep(1000) job?.cancel() println( "btn_right 结束协程") }
执行结果(本该执行100轮的打印,只持续了10轮):
协程正在执行 0 协程正在执行 1 协程正在执行 2 协程正在执行 3 协程正在执行 4 协程正在执行 5 协程正在执行 6 协程正在执行 7 协程正在执行 8 协程正在执行 9 btn_right 结束协程 Process finished with exit code 0
-
如果你想让一个任务最多执行3秒,超过3秒则自动取消
import kotlinx.coroutines.* fun main() = runBlocking { println("限时任务中结果是:" + getResFromTimeoutTask()) } suspend fun getResFromTimeoutTask(): String? { // 忘了,它会保证内部的协程代码都执行完毕,所以不能这么写 return withTimeoutOrNull(1300) {
3秒则自动取消
import kotlinx.coroutines.*
fun main() = runBlocking {
println("限时任务中结果是:" + getResFromTimeoutTask())
}
suspend fun getResFromTimeoutTask(): String? {
// 忘了,它会保证内部的协程代码都执行完毕,所以不能这么写
return withTimeoutOrNull(1300) {