肢解协程:Dispatchers.Main到底是个啥(一)?

739 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

大家在使用协程的时候,对应Dispatchers.Main并不陌生,将协程的上下文指定为Dispatchers.Main,就代表协程代码块将在主线程中执行。但你有没有想过,Dispatchers.Main底层是怎么实现这种能力的,即如何将协程代码放到主线程中执行的?Dispatchers.Main.immediate又是用来干什么的?

本篇文章主要是回答三个问题:

  1. Dispatchers.Main是个啥?

  2. Dispatchers.Main是怎么实现的?

  3. Dispatchers.Main如何使用?

  4. Dispatchers.Main.immediateDispatchers.Main又有啥区别?

Dispatchers.Main是个啥?

这个问题很容易回答,通过打印Dispatchers.Main::class.simpleName可知,Dispatchers.Main就是个HandleContext类的一个实例对象。

internal class HandlerContext private constructor(
    private val handler: Handler,
    private val name: String?,
    private val invokeImmediately: Boolean
): HandlerDispatcher()

其中HandlerContext的两个关键参数,在这里直接说结论:

  • handler持有的mLooper是主线程Looper.getMainLooper()

  • invokeImmediately值为false,默认任何场景通过调度器分发到主线程执行(即使当前已经是主线程环境)

接下来我们一步步分析Dispatchers.Main实现流程,以及上面两个参数在协程调度中怎样发挥作用。

Dispatchers.Main是怎么实现的?

接下来进行Dispatchers.Main的创建流程分析:

Dispatchers.Main

public actual object Dispatchers {

    @JvmStatic
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
}

MainDispatcherLoader.dispatcher

@JvmField
val dispatcher: MainCoroutineDispatcher = loadMainDispatcher()

private fun loadMainDispatcher(): MainCoroutineDispatcher {
    return try {
        val factories = if (FAST_SERVICE_LOADER_ENABLED) {
            FastServiceLoader.loadMainDispatcherFactory()
        } else {
            ServiceLoader.load(
                    MainDispatcherFactory::class.java,
                    MainDispatcherFactory::class.java.classLoader
            ).iterator().asSequence().toList()
        }
        factories.maxByOrNull { it.loadPriority }?.tryCreateDispatcher(factories)
            ?: createMissingDispatcher()
    }
}

不管FAST_SERVICE_LOADER_ENABLED为true还是false,最终都是创建一个实现了MainDispatcherFactory接口的AndroidDispatcherFactory类型的实例对象。

PS: 当FAST_SERVICE_LOADER_ENABLED为true,是通过反射创建的AndroidDispatcherFactory;为false,则是通过java的SPI服务发现机制创建的AndroidDispatcherFactoryServiceLoader.load会读取META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory文件:

image.png

接下来分析AndroidDispatcherFactory

AndroidDispatcherFactory

internal class AndroidDispatcherFactory : MainDispatcherFactory {

    override fun createDispatcher(allFactories: List<MainDispatcherFactory>): MainCoroutineDispatcher {
        val mainLooper = Looper.getMainLooper() ?: throw IllegalStateException("The main looper is not available")
        return HandlerContext(mainLooper.asHandler(async = true))
    }

最终调用其createDispatcher()方法创建了HandlerContext:

  1. 获取主线程Looper并调用asHandler()扩展方法创建一个异步Handler对象:
internal fun Looper.asHandler(async: Boolean): Handler {
    if (!async || Build.VERSION.SDK_INT < 16) {
        return Handler(this)
    }
    //28及以上提供了createAsync创建异步Handler
    if (Build.VERSION.SDK_INT >= 28) {
        val factoryMethod = Handler::class.java.getDeclaredMethod("createAsync", Looper::class.java)
        return factoryMethod.invoke(null, this) as Handler
    }
    //通过构造方法创建异步Handler
    val constructor: Constructor<Handler>
    try {
        constructor = Handler::class.java.getDeclaredConstructor(Looper::class.java,
            Handler.Callback::class.java, Boolean::class.javaPrimitiveType)
    } catch (ignored: NoSuchMethodException) {
        return Handler(this)
    }
    return constructor.newInstance(this, null, true)
}

这个创建异步Handler扩展考虑的非常全面标准,包括了对各个变动版本的适配,大家可以考虑把这个扩展移植到自己项目中使用。

  1. 构造HandlerContext

image.png

handler的Looper就是主线程LooperinvokeImmediately赋值false。

我们看下HandlerContext的继承链:

stateDiagram-v2
[*] --> HandlerContext

HandlerContext --> HandlerDispatcher
HandlerDispatcher --> MainCoroutineDispatcher

MainCoroutineDispatcher --> CoroutineDispatcher
CoroutineDispatcher --> ContinuationInterceptor

ContinuationInterceptor --> [*]

属于协程四大上下文元素之一的分发器。

总结

我们先回答一开始提出的前两个问题:

  1. Dispatchers.Main是个HandlerContext对象

  2. HandlerContext是被AndroidDispatcherFactory创建的,而AndroidDispatcherFactory是通过SPI或者反射创建的,其中参数invokeImmediately为false,handler分发消息到主线程。


接下来还有一篇文章分析HandlerContext在协程源码中是如何进行运用的。