Kotlin协程操作之创建启动挂起恢复详解 目录 一.协程的创建 1.start方法 2.CoroutineStart类 3.startCoroutineCancellable方法 4.createCoroutineUnintercepted方法 5.createCoroutineFromSuspendFunction方法 二.协程的启动 1.ContinuationImpl类 2.resumeCancellableWith方法 3.BaseContinuationImpl类
目录
- 一.协程的创建
- 1.start方法
- 2.CoroutineStart类
- 3.startCoroutineCancellable方法
- 4.createCoroutineUnintercepted方法
- 5.createCoroutineFromSuspendFunction方法
- 二.协程的启动
- 1.ContinuationImpl类
- 2.resumeCancellableWith方法
- 3.BaseContinuationImpl类
- 4.invokeSuspend方法
- 三.协程的挂起与恢复
下面以launch方法为例进行分析。
一.协程的创建
launch方法的代码如下:
newCoroutineContext用于计算新的上下文,代码如下:
1.start方法
在不指定协程启动模式的情况下,协程将按照DEFAULT模式启动,在上述代码中,会调用StandaloneCoroutine对象的start方法。StandaloneCoroutine的代码如下:
StandaloneCoroutine类中仅重写了handleJobException方法,用于处理父协程不处理的异常。因此这里调用的start方法实际是父类AbstractCoroutine的方法,AbstractCoroutine类的start方法代码如下:
AbstractCoroutine类的start方法内,调用了CoroutineStart类的invoke方法。
2.CoroutineStart类
CoroutineStart是一个枚举类,用于根据不同的启动模式去启动协程,代码如下:
CoroutineStart类中有两个invoke方法,其中一个参数中有receiver,另一个没有receiver。在Kotlin协程中,很多方法都重载了带有receiver的方法和不带有receiver的方法。
receiver用于为block执行提供一个环境。Kotlin中提供的启动协程的方法都是通过带receiver参数的start方法实现。通过receiver环境,可以更方便的实现一些操作,比如在launch启动的协程中再次调用launch启动新的协程。在没有receiver的环境下执行block,则更像是在suspend方法中执行,如果需要启动其他的协程,需要自己提供环境。
3.startCoroutineCancellable方法
startCoroutineCancellable是一个扩展方法,用来创建一个可以取消的协程,代码如下:
4.createCoroutineUnintercepted方法
createCoroutineUnintercepted方法用于创建一个新的、可挂起的、不受干扰的协程。
在Kotlin中有很多被expect关键字标记的接口方法,需要找到对应平台下被actual标记的实现方法。
createCoroutineUnintercepted方法创建的协程需要手动调用resumeWith方法才可以启动,但重复的调用resumeWith方法可能会导致状态机发生异常。同时,参数中传入的completion可能会在任意的上下文中被调用。
正常情况下,我们编写的lambda表达式——block,在编译器编译时,会自动生成一个类,并继承SuspendLambda类,实现Continuation等接口。因为SuspendLambda继承自ContinuationImpl,ContinuationImpl继承自BaseContinuationImpl,所以才有了上述代码中的判断逻辑。
如果当前的block对象的类型为BaseContinuationImpl,则调用create方法,这里的create方法是编译器生成的类里的重写方法,它的内部就是通过我们传入的参数,创建并返回根据blcok生成的类的一个实例对象。
如果当前的block对象的类型不为BaseContinuationImpl,则需要通过createCoroutineFromSuspendFunction方法创建协程。这里假设lambda表达式的类型不是BaseContinuationImpl。
5.createCoroutineFromSuspendFunction方法
该方法用于在createCoroutineUnintercepted方法中使用,当一个被suspend修饰的lambda表达式没有继承BaseContinuationImpl类时,则通过此方法创建协程。
有两种情况会调用该方法创建协程:第一种情况是lambda表达式中调用了其他的挂起方法;第二种情况是挂起方法是通过Java实现的。
createCoroutineFromSuspendFunction方法的代码如下:
受限协程是指协程在运行过程中的,只能调用协程作用域中提供的挂起方法发生挂起,其他挂起方法不能调用,因为在挂起方法会对续体进行拦截,可能导致后续代码的执行变得无法预测。
典型的例子就是sequence方法,它创建的协程就是受限协程,只能通过调用yield方法或者yieldAll方法才能发生挂起。由于受限协程中不能进行协程调度,因此其上下文是空的。
这里launch方法的上下文有一个默认调度器,因此会创建一个ContinuationImpl对象。
到这里,协程完成了创建。
二.协程的启动
再次回到startCoroutineCancellable方法,当调用createCoroutineUnintercepted创建好协程后,会调用intercepted方法,代码如下:
intercepted方法是Continuation接口的扩展方法,内部调用了ContinuationImpl类的intercepted方法。
1.ContinuationImpl类
这里的ContinuationInterceptor指的就是在newCoroutineContext方法中传入的Dispatchers.Default调度器。CoroutineDispatcher类的interceptContinuation方法的代码如下:
2.resumeCancellableWith方法
再次回到startCoroutineCancellable方法,当调用intercepted方法进行拦截后,会调用resumeCancellableWith方法,代码如下:
由于当前的Continuation对象的类型为DispatchedContinuation,因此调用DispatchedContinuation类的resumeCancellableWith方法,代码如下:
runUnconfinedEventLoop方法是一个扩展方法,用于启动EventLoop,代码如下:
Dispatchers.Default调度器与Dispatchers.Unconfined调度器的调度逻辑基本都相同,最终都是调用Contination对象的resumeWith方法,同时传入Result对象作为参数。
这里的Contination是createCoroutineUnintercepted方法创建的继承ContinuationImpl的匿名内部类对象。Result是resumeCancellableWith方法传入的Result.success(Unit)对象,因为首次启动,所以传入类型为Unit。
调用匿名内部类的resumeWith方法,实际调用的是父类BaseContinuationImpl的resumeWith方法。
3.BaseContinuationImpl类
BaseContinuationImpl类的resumeWith方法的代码如下:
4.invokeSuspend方法
在上述代码中,resumeWith方法内部调用了invokeSuspend方法,这里的invokeSuspend方法实际就是createCoroutineFromSuspendFunction方法中创建的匿名内部类的invokeSuspend方法。匿名内部类的代码如下:
三.协程的挂起与恢复
通过上述代码的分析,协程的挂起实际就是在协程返回结果时返回一个COROUTINE_SUSPENDED对象,在收到COROUTINE_SUSPENDED结果后直接返回,等待被再次调用resumeWith恢复。
COROUTINE_SUSPENDED对象定义在枚举类CoroutineSingletons中,代码如下:
该枚举类代表了协程的三个状态,协程在创建后状态为UNDECIDED,如果执行过程中发生挂起,则状态变为COROUTINE_SUSPENDED,最后挂起恢复后状态变为RESUMED。
而协程的恢复实际就是在挂起方法执行完成后,通过调用协程执行时传入的续体的resumeWith方法,恢复后续代码的执行。
到此这篇关于Kotlin协程操作之创建启动挂起恢复详解的文章就介绍到这了,更多相关Kotlin协程操作内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!