先来一个例子: 日志如下: 可以看到上面先是执行了withContext中的代码,然后执行了launch中的代码,注意到launch中也是在单独线程中执行的,其中launch中使用的Dispatcher是Dispatchers.Default类型的。前面分析过launch调度情况,最后会执行到continuation的resumeWith,然后调用它的invokeSuspend方法: 可以看到先执行withContext,因为withContext启动的时候,默认的状态是COROUTINE_SUSPENDED,所以退出了,返回了var2,此时的label=1了。当执行完withContext的时候,会通知传给withContext的continuation,也就是上面的CoroutineDispatchersActivity$demo2$1这个SuspendLambda。所以会再次执行它的invokeSuspend方法,此时lable=1,所以最后返回了Unit.INSTANCE,整个invokeSuspend结束,这就是挂起的原因。 至于为什么在CoroutineDispatchersActivity$demo2$1中遇到了返回值为COROUTINE_SUSPENDED时候,不会继续执行了呢?看下BaseContinuationImpl中的resumeWith逻辑: 在BaseContinuationImpl中的resumeWith中,如果invokeSuspend返回值是COROUTINE_SUSPENDED,则直接return了,不往上层的continuation调用了。
至于再次执行CoroutineDispatchersActivity$demo2$1的invokeSuspend方法是在什么时候,可以追溯到withContext中: 执行了block的startCoroutineCancellable: 此处很熟悉啊,这不又启动了一个协程吗?协程套协程啊。好吧,我们直接看completion的回调,在协程挂起函数执行完后,会执行complete的resumeWith方法,此处是DispatchedCoroutine对象,继承自ScopeCoroutine: 它是先取continuation中context的ContinuationInterceptor,也就是上面launch启动的dispatchers,对应的是Dispatchers.Default,最终执行Dispatchers的resumeCancellableWith方法,在该方法里面最终会在协程指定的线程中执行协程代码块。所以在上面日志中能看到withContext执行完后,launch之后的代码能回到指定的线程中执行。
withcontext执行完了后会执行外面协程的resumeWith,所以launch的invokeSuspend再次执行。再次执行的时候会回到自己的线程。真相大白了!