关于Android LifecycleScope的一点点发现

语言: CN / TW / HK

例子

image-20220119150748335

代码的运行顺序应该是one(),two()还是two(),one(),或者是两个调用的时机是随机的,看协程的调度呢?相信很多人都会选择最后一个.但实际无论运行多少次都是下面的结果

image-20220119151317244

并且launch()方法传入的suspend 函数甚至会阻塞主线程,怎么回事儿,这个协程是假的协程?吗!

先说结论

Kotlin的协程在不同的平台有不同的实现方式.本文以我最熟悉的Android平台为例.在Android中,启动一个协程使用Dispatcher.Main.immediate作为调度器的话,如果当前线程是主线程,就会将任务立即在主线程执行,如果是在其它线程中,使用Handler.post()方法分给主线程去执行.

如果是Dispatcher.Main作为调度器,就会直接使用主线程的Handler.post()去执行.

CoroutineDispatcher:协程调度器

image-20220119110709671

image-20220119110813517

在导入了androidx.lifecycle:lifecycle-runtime-ktx依赖后,可以在Activity中使用lifecycleScope.launch{}启动一个协程,这里默认的上下文就是 EmptyCoroutineContext,因此启动的协程上下文就是

kotlin lifecycleScope.coroutineContext+EmptyCoroutineContext

看下lifecycleScope是怎么来的

image-20220119145648128

这样lifecycleScope.launch{}这样启动一个协程默认的调度器就是Dispatcher.Main.immediate

Dispatcher顾名思义就是调度器,在这里就是负责协程的调度功能,导入依赖查看Android平台的Disptcher.Main的最终实现是由HandlerContext实现的

image-20220119115307629

HandlerContext重写了isDispatchNeeded方法

image-20220119115553319

查看isDispatchNeeded的注释可知道如果该方法返回true就会通过dispatch()将协程分发出去,false就会令协程在当前线程恢复,因此Disptcher.Main会始终调用dispather()来进行分发,而Dispatcher.Main.immediate只有在非主线程中会调用dispatch().何时会调用CoroutineDispatcher.isDispatchNeeded()方法呢?通过查看函数调用在DispatchedContinuation.resumeWith()方法中使用到了它.

DispatchedContinuation:协程调度的发起者

这里只关注DispatchedContinuation是如何让协程立即在当前线程恢复的逻辑

image-20220119142039471

image-20220119143447268

executeUnconfined()方法是DispatchedContinuation的一个拓展函数,协程的执行是交给EventLoop处理,它类似Handler的MessageQueue,如果当前线程的队列已满就会使用EventLoop.dispatchUnconfined()将这个协程调度到当前的EventLoop中,否则直接调用runUnconfinedEventLoop()将协程执行