Hi你好,新同窗。很高兴,你终于追寻这个问题了,也许你正感到迷茫,各路大神对协程的理解不一,有人说它是线程框架,有人说它比线程更轻,但愿我这篇博文能够帮你从另外一个角度简单理解协程。html
请相信一句话,任何解释从第二我的口中说出时,可能已经存在了变化。而官网是咱们接触任何技术最必要的门槛。因此请打开Kotlin中文网。不少人说kotlin官网教程很不详细,其实否则,kotlin中文网教程很详细。回到正题:java
注意上面几个关键点和一些实际使用,不难明白android
Kotlin协程是基于Kotlin语法从而延伸的一个异步编程框架,它并无带来多少性能上的体验,它能实现的,你用线程池一样也能够实现,但对于使用角度的来讲,协程努力打造一个 "同步方式,异步编程的" 思想,做为开发者来讲,咱们能够更懒了,切换线程,withContext便可,协程带来了开发上的温馨,但这种温馨是基于 Kotlin 的语法,并非别的。因此我但愿你们关注协程时,多从语言角度去理解。编程
协程就是一个基于Kotlin语法的异步框架,它可使开发者以同步的方式,写成异步的代码,而无需关注多余操做。就这么简单bash
suspend fun main() {
GlobalScope.launch {
println("启动--${System.currentTimeMillis()}---${Thread.currentThread().name}")
delay(100)
println("挂起100ms--${System.currentTimeMillis()}---${Thread.currentThread().name}")
}
delay(1000)
}
复制代码
启动--1579085375166---DefaultDispatcher-worker-1
挂起100ms--1579085375275---DefaultDispatcher-worker-1
复制代码
fun main() {
runBlocking(Dispatchers.IO) {
println("启动--${System.currentTimeMillis()}")
val as1 = async {
delay(100)
println("并发1--${System.currentTimeMillis()}---${Thread.currentThread().name}")
"1"
}
val as2 = async {
delay(100)
println("并发2--${System.currentTimeMillis()}---${Thread.currentThread().name}")
"2"
}
println("as1=${as1.await()},as2=${as2.await()}")
}
}
复制代码
启动--1579085205199
并发1--1579085205313---DefaultDispatcher-worker-3
并发2--1579085205313---DefaultDispatcher-worker-2
as1=1,as2=2
复制代码
观察上面demo的运行结果,是否是很舒服,看起来同步的方式内部倒是在异步操做。那上面注释中 挂起 是什么意思呢?并发
观察上面的打印日志,咱们不难发现,在调用 delay 函数时,线程并无停下,相对来讲,只是咱们的协程代码块被挂起,等待恢复。只有前面的挂起函数执行结束,咱们的协程代码块才能继续执行。借用一幅图来讲明以下:框架
因此所谓的挂起实际上是代码层次的一个处理,从而使得咱们能够以同步形式去写异步的代码。异步
所谓的非阻塞,其实就是切换了线程,观察打印日志变化,咱们能够发现,当咱们直接 GlobalScope.launch 启动一个协程时,此时运行的线程为默认的线程,因此协程被称为非阻塞的实现方式。async
先看下面的图ide
当使用suspend修饰后的函数咱们称其为挂起函数,那么挂起函数有什么做用?为何test 的suspend 标志是黑的?
咱们再继续往下看:看一下java字节码
这个 Continuation是什么呢?按照字面意思,意思为延续。那咱们该怎么理解呢?
通常来讲 Continuation 表明的是<剩余的计算的概念>,也就是 接下来要执行的代码。官方的解释叫作 挂起点
好比我有一段代码:
println("123".length)
复制代码
最早执行的是"123".length,而后执行 println打印长度,此时执行 "123".length 就能够当作一个挂起点,也就是代码从这里中止,等待计算出结果,然而此时内部线程却没有中止,当计算完的时候,也就是挂起结束,此时接着执行咱们的打印语句。
切换到咱们的suspend中,它表明的就是一个***标志*** 的做用,有suspend修饰的表明的函数叫作挂起函数,当编译器碰到这个标志的函数,就知道它是一个可能会耗时的操做。
由于普通函数参数并无带 Continuation啊,至关于没有挂起点,编译器没法判断,因此此时会报错。
编译器知道它是挂起函数,可是test内部没有挂起函数啊,因此此时编译器提示。
那这个函数就一点做用没有?
有做用,就是限制这个函数只能挂起函数调用。
class Main3Activity : AppCompatActivity() {
private val mainScope = CoroutineScope(Dispatchers.Default)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main3)
progressBar.max = 100
mainScope.launch {
(1..100).forEach {
delay(100)
withContext(Dispatchers.Main) {
progressBar.secondaryProgress = it
}
}
}
}
onDestory(){
//记得关闭
}
}
复制代码
class MainViewModel : ViewModel() {
fun test() {
viewModelScope.launch {
}
}
}
复制代码
在ViewModel取消时,协程将自动关闭
导入如下依赖
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-rc03" 复制代码
因而刚才的倒计时代码改成以下:
class Main3Activity : AppCompatActivity() {
....
lifecycleScope.launch {
...
}
}
}
复制代码
一样,当fragment或者Activity关闭时,协程一样将自动关闭。
查看源码,会发现,viewModel中的 viewModelScope 和 Lifecycle lifecycleScope,实现方式一模一样:
本篇,咱们没有过多的从源码上去追寻,协程究竟是什么,尽可能从语法,使用者的角度入手,带着你们从侧面去理解协程,但愿你们都能有本身的理解。
若是但愿深刻研究,推荐如下博客: