kotlin协程基础分享

参考文章:kotlin官方中文文档
Kotlin Primer·第七章·协程库(上篇)

协程是什么

  • 协程

用户态线程将控制权交给进程,让进程调度自己,所以从本质上来说,协程是线程。

  • 用户态线程

在进程中手写代码去管理线程的逻辑调度,这就是用户态线程。

为什么使用协程

Android中一个比较典型的例子就是网络请求:

  1. 主线程发起网络请求任务;
  2. 子线程请求服务端相应;
  3. 等待网络传递请求;
  4. 等待服务器处理并传回数据;
  5. 子线程获取服务器返回数据并转换格式;
  6. 主线程执行某个回调。

而在2-4这个过程中,线程是处于阻塞状态的,假设在该线程中还有一些与网络处理无关的逻辑,则此时的线程利用率就不是很高了,这时就可以引入协程的概念,当该线程中的协程挂起时,线程依旧可以继续做事,因此引入协程,可以极大程度的复用线程。

怎么使用协程

  1. kotlin的协程并没有包含在核心库里,因此若要使用协程,需要将他们引入:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'
  1. 直接从第一个协程程序讲起
 GlobalScope.launch { // 在后台启动一个新的协程并继续
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活,这里我自己是不太明白

GlobalScope是CoroutineScope上下文的子类,launch是我们现在接触到的第一个协程构建器,GlobalScope.launch{}表示启动了一个新协程,该协程的生命周期只受整个应用程序的生命周期限制。

  1. 在第一个协程程序做一点小小的改动
GlobalScope.launch { // 在后台启动一个新的协程并继续
        delay(1000L)
        println("World!")
    }
    println("Hello,") // 主线程中的代码会立即执行
    runBlocking {     // 但是这个表达式阻塞了主线程
        delay(2000L)  // ……我们延迟 2 秒来保证 JVM 的存活
    } 

我们将阻塞线程的Thread.sleep(2000L)改成runBlocking的协程代码,我们就接触到了第二个协程构建器runBlocking,主线程调用runBlocking会一直阻塞直到runBlocking内部的协程执行完毕。

  1. 3向我们展示了在主线程中启动两个协程,那我们再做下优化。
runBlocking<Unit> { // 开始执行主协程
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        delay(1000L)
        println("World!")
    }
    println("Hello,") // 主协程在这里会立即执行
    delay(2000L)      // 延迟 2 秒来保证 JVM 存活
}

主线程不再执行println,也不再启动两个协程,而是运行一个协程,在该顶层主协程内,启动一个新协程,并执行打印和delay操作

  1. 对2的拓展
  • 作用域构建器

除了上述提到的runBlocking,还可以使用coroutineScope声明作用域。runBlocking和coroutineScope主要的不同之处在于后者在等待所有的子协程执行完毕时并没有阻塞当前线程。

runBlocking { // this: CoroutineScope
    launch { 
        delay(200L)
        println("Task from runBlocking")
    }
    
    coroutineScope { // 创建一个新的协程作用域
        launch {
            delay(500L) 
            println("Task from nested launch")
        }
    
        delay(100L)
        println("Task from coroutine scope") // 该行将在嵌套启动之前执行打印
    }
    
    println("Coroutine scope is over") // 该行将在嵌套结束之后才会被打印
}
  • launch的返回值是个Job对象,该对象有常用的3个方法:start、join和cancel,分别对应协程的启动、切换至当前协程、取消。
val job = GlobalScope.launch { // 启动一个新协程并保持对这个作业的引用
    delay(1000L)
    println("World!")
}
println("Hello,")
job.join() // 等待直到子协程执行结束

该结果打印的仍然是Hello,World!如果我改成

val job = GlobalScope.launch { // 启动一个新协程并保持对这个作业的引用
    delay(1000L)
    println("World!")
}
job.join() // 等待直到子协程执行结束
println("Hello,")

打印的结果就是World!Hello,这样你就更能理解join的用法了

遇到的问题

runBlocking中嵌套coroutineScope时,coroutineScope会等待所有子协程执行完毕,而且不会阻塞当前线程的执行(换句话说,runBlocking会等待coroutineScope协程块中的代码执行完毕),才会继续执行接下去的代码

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值