这章主要想剖析下协程的结构,如果你已经在项目中用过协程,那么我想用过最多的方式有可能是如下俩种:
viewModelScope.launch{
......
}
GlobalScope.launch{
......
}
至于 viewModelScope 和 GlobalScope 是什么,launch 方法是什么,为什么在 launch 中就可以执行了 suspend 方法了等等这些问题可能并没有那么深入的了解。
其实上边那些问题都是协程的基础,如果不了解的话,那所使用的‘协程’也只能是人云亦云,别人怎么写我就怎么写而已,很难做到一丁儿点改变,就更别说优化。
这一章的内容是了解概念,抛开系统的封装,让我们来看下原始的协程
要了解协程的话,首先我们要创建一个协程,像上边内两种创建协程的方法 viewModelScope.launch
、 GlobalScope.launch
都是系统封装后的,而现在要做的就是抛开封装,自己创建。
下面,这是两个创建协程的方法源码:
Continuation.kt
/**
* Creates a coroutine without a receiver and with result type T. This function creates a new, fresh instance of
* suspendable computation every time it is invoked.
*
* To start executing the created coroutine, invoke resume(Unit) on the returned Continuation instance.
* The completion continuation is invoked when the coroutine completes with a result or an exception.
* Subsequent invocation of any resume function on the resulting continuation will produce an IllegalStateException.
*/
@SinceKotlin("1.3")
@Suppress("UNCHECKED_CAST")
public fun <T> (suspend () -> T).createCoroutine(
completion: Continuation<T>
): Continuation<Unit> =
SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
/**
* Creates a coroutine with receiver type [R] and result type [T].
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
*
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
* The [completion] continuation is invoked when the coroutine completes with a result or an exception.
* Subsequent invocation of any resume function on the resulting continuation will produce an [IllegalStateException].
*/
@SinceKotlin("1.3")
@Suppress("UNCHECKED_CAST")
public fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> =
SafeContinuation(createCoroutineUnintercepted(receiver, completion).intercepted(), COROUTINE_SUSPENDED)
这两个创建协程的方法一个是带作用域一个不带作用域,像我们用的 viewModelScope.launch 实际上就是第二种带作用域的方式,下面两段代码是两个创建实例:
不带作用域的:
fun myCreateCoroutine() {
val myFun = suspend {
println("In SuspendFunction")
delay(1000)
100
}
val continuation = myFun.createCoroutine(object : Continuation<Int> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Int>) {
println("in resumeWith$result")
}
})
continuation.resume(Unit)
}
带作用域的协程创建:
// 创建启动一个有作用域的协程的函数
fun <R, T> launchCoroutine(receiver: R, block: suspend R.() -> T) {
val continuation = block.createCoroutine(receiver, object : Continuation<T> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<T>) {
println("in resumeWith$result")
}
})
continuation.resume(Unit)
}
// 创建一个作用域
class ProducerScope<T> {
suspend fun produce(value: T) {
delay(100)
println("in produce $value")
}
}
// 测试代码
fun createReceiverCoroutine() {
launchCoroutine(ProducerScope<Int>()) {
println("In Coroutine")
produce(1024)
delay(1000)
produce(2048)
}
}
测试代码:
fun main() {
startLoop()
myCreateCoroutine()
println("---------- code end")
}
fun myCreateCoroutine() {
println("---------- in createCoroutine start")
val myFun = suspend {
println("In SuspendFunction")
delay(1000)
100
}
val continuation = myFun.createCoroutine(object : Continuation<Int> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Int>) {
println("in resumeWith$result")
}
})
continuation.resume(Unit)
println("---------- in createCoroutine end")
}
fun startLoop() {
Thread {
while (true) {
Thread.sleep(100)
}
}.start()
}
//-------- 测试结果输出-------
//---------- in createCoroutine start
//In SuspendFunction
//---------- in createCoroutine end
//---------- code end
//in resumeWithSuccess(100)
希望你看到这还没有走,上面库库库一段代码不免让人看的云里雾里的,但是总得有个切入点。不知道你们看创建协程的源代码的时候有什么疑问,反正我看的时候就很三个很疑惑的地方:
- 创建协程我到底创建了个啥,咋用?
suspend () -> T
和suspend R.() -> T
是个 Lambda,而createCoroutine
函数为这两个 Lambda 的扩展函数,那么这两个 Lambda 到底是啥,它的内容是什么时候被执行的?- 这个
Continuation
是什么,为什么参数里有它返回值还是它,这俩什么关系?
首先说创建协程创建了什么
单说 myCreateCoroutine
函数,它内部创建了一个协程运行载体,刚写协程的时候我们都知道 suspend 函数规定必须要在 suspend 修饰的函数内执行,那么问题出现了,第一个 suspend 是怎么出来的,这个就是载体。例如下面代码:
fun selfFun(){
val suspendFunction = suspend {
println("In SuspendFunction")
delay(1000)
println("In SuspendFunction End")
}
suspendFunction.startCoroutine(object : Continuation<Unit> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
println("Coroutine End: $result")
}
})
}
fun globalFun(){
GlobalScope.launch {
println("In SuspendFunction")
delay(1000)
println("In SuspendFunction End")
}
}
对比下上述两个函数,如果只看载体相关的话,两个作用是一样的,都是定义了一个 suspend 载体,并且在里面运行其他的 suspend 的函数(delay)。
所以对于创建协程,我的理解是创建了一个协程载体可运行的环境
上边通过非常粗暴的方式解释了创建协程是什么有什么用(粗暴的理解为 GlobalScope.launch 的内部实现),那么我们肯定是对这个结果不满意的,丢失了太多的细节以至于跟没说一样。这个不是学习的结果和目的,为了实现熟练掌握协程,不能在一开头就草草而过。
那么接下来我们一步一步解开创建协程的秘密,首先是 suspend 的关键词
Suspend 方法
在讲 suspend 函数之前,我们先补下 Kotlin 的知识,在 Kotlin 中Lambda 到底是什么,下面是我新建的一个 LambdaTest.kt 的文件,并写入如下代码:
fun main() {
val noSuspendLambda = {
println("in noSuspendLambda")
}
}
这段代码很简单,就是创建了一个 Lambda,那么它是如何符合 jvm 规范的呢,想破头脑不如反编译一下,如下图:
通过反编译以后的字节码,可以看到 noSuspendLambda 被编译器加工成了一个类,继承了 Lambda 并且实现了 Function0。
有了如上信息后,我猜你已经知道如何去看 suspend lambda 到底是什么了,废话不多说,修改下代码:
fun main() {
val suspendLambda = suspend {
println("in suspendLambda")
}
val suspendFun:(suspend () -> Unit) = ::suspendFun
}
suspend fun suspendFun(){
println("in suspendFun")
}
反编译后的截取
通过