协程,全称可以译作协同程序,很多语言都有这个概念和具体实现,之前入门Python的时候接触过,而Kotlin其实也早就有这个扩展功能库了,只不过之前一直处于实验阶段,不过前段时间1.0的正式版终于出了,网上的相关博客也多了起来,经过这几天的学习我也来做下小结吧。
环境配置
首先贴下Kotlin协程的官方github地址,下面的配置都是参照这里的说明,而且里面还贴心的给我们准备了很多基础的,感兴趣的的小伙伴稍后可以去看看。
首先配置下Kotlin版本
buildscript { ext.kotlin_version = '1.3.11'}复制代码
然后引入依赖,目前最新版是1.1.0
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.0'复制代码
配置很简单,接下来干什么呢。当然是写个协程版的Hello World
了!
import kotlinx.coroutines.*fun main() { GlobalScope.launch { // 创建并启动一个协程 delay(1000L) // 延迟(挂起)1000毫秒,注意这不会阻塞线程 println("World!") //延迟之后执行打印 } println("Hello,") // 协程延迟的时候不会影响主线程的执行 Thread.sleep(2000L) // 阻塞线程2s,保证JVM存活,协程可正常执行完}复制代码
运行结果:
2018-12-23 17:35:16.998 15539-15539/com.renny.kotlin I/System.out: Hello,2018-12-23 17:35:18.005 15539-18893/com.renny.kotlin I/System.out: World!复制代码
基础语法
启动模式
上面的协程启动模式是默认的DEAFAULT
,也就是创建并立即启动的,我们也可以设置启动模式为LAZY
,来自己安排是什么时候需要启动:
fun main() { val job = GlobalScope.launch(start = CoroutineStart.LAZY) { println("World!") } println("Hello,") job.start() Thread.sleep(2000L) }复制代码
在我所采用的Kotlin 1.3版本中,还有ATOMIC
和UNDISPATCHED
两个额外的模式,但是现在还是实验版,这里不多介绍。结果如上。CoroutineScope.launch
一共有三个参数,然后介绍其他两个:
context: CoroutineContext = EmptyCoroutineContext
:协程上下文block: suspend CoroutineScope.() -> Unit
:闭包参数,定义协程内需要执行的操作。
返回值为Job对象。
Job类
通过上面的例子,我们知道了一个重要的点launch
函数是有返回值的,它是一个Job
的接口类型,除了配合LAZY
来自己启动一个协程,下面介绍下其他几个重要方法:
job.cancel()
取消一个协程
fun main() { val job = GlobalScope.launch { delay(1000L) println("World!") } job.cancel() println("Hello,") }复制代码
协程被取消了,所以只打印了Hello,
join()
等待协程执行完毕
fun main() = runBlocking { val job = GlobalScope.launch { delay(1000L) println("World!") delay(1000L) } println("Hello,") job.join() println("Good!")}复制代码
作用很像Thread.join()
函数,join()
后面的代码会等到协程结束再执行,结果如下:
2018-12-24 21:19:41.153 23484-23484/com.renny.kotlin I/System.out: Hello,2018-12-24 21:19:42.148 23484-24172/com.renny.kotlin I/System.out: World!2018-12-24 21:19:43.161 23484-23484/com.renny.kotlin I/System.out: Good!复制代码
job.cancelAndJoin()
等待协程执行完毕然后再取消 这是一个Job
的扩展函数,它结合了cancel
和join
的调用,来看下它的实现:
public suspend fun Job.cancelAndJoin() { cancel() return join()}复制代码
挂起函数
细心的同学可能发现了两个不通点,Job.join()
函数被一个名字叫runBlocking
的包围了,而Job.start()
和Job.cancel
都没有;Job.cancelAndJoin()
前面被一个特殊的关键词suspend
修饰了,这有什么用呢?
其实通过查看源码,Job.join()
也被suspend
修饰了,所以这是一个suspend
(挂起)函数,挂起函数必须在协程中或者挂起函数中使用,因为调用了Job.join()
,Job.cancelAndJoin()
也必须加上suspend
声明。事实上,要启动协程,必须至少有一个挂起函数。
协程及协程挂起:
协程是通过编译技术实现的,不需要虚拟机VM/操作系统OS的支持,通过相关代码来生效协程的挂起几乎无代价,无需上下文切换或涉及OS协程不能在随机指令中挂起,只能在挂起点挂起(调用标记函数)!复制代码
子协程
上面我们都是在线程中开启一个协程,同样在协程中我们也能开启另一个协程,所以我们再来看下复杂点的例子:
fun main() = runBlocking { GlobalScope.launch { delay(1000L) println("World!") } println("Hello,") runBlocking { delay(2000L) } }复制代码
最外层的runBlocking
为最高级的协程 (一般为主协程), 其他协程如launch {}
因为层级较低能跑在runBlocking
里。runBlocking
的最大特点就是它的delay()
可以阻塞当前的线程,和Thread.sleep()
有着相同的效果。打印的日志同第一个示例。
Job类中会存储子协程的集合:
public val children: Sequence复制代码
同样也提供了取消全部子协程的方法:
public fun Job.cancelChildren() { children.forEach { it.cancel() }}复制代码
小结
这篇主要介绍了协程引入Android项目的配置,协程的一些基本操作,挂起函数的概念,大家对协程有一个基本的概念,下一篇将讲下协程的更多知识。