Kotlin uses ServiceLoader to load MainCoroutineDispatcher class, which generates an AndroidDispatcherFactory using Looper.getMainLooper() when you import kotlinx-coroutines-android.
We know CoroutineDispatcher determines what thread or threads the corresponding coroutine uses for its execution.
Inside Kotlin coroutines core, the single object Dispatchers will create each default CoroutineDispatchers
, e.g IO, Unconfied and Main. In Android, when depending on org.jetbrains.kotlinx:kotlinx-coroutines-android, this Main will be a Handler with the main looper.
The abstract MainDispatcherFactory
will be inherited in different libraries. In kotlinx-coroutines-android, the inherited one is AndroidDispatcherFactory
, and in kotlinx-coroutines-swing, it is SwingDispatcherFactory
.
internal class AndroidDispatcherFactory : MainDispatcherFactory {
companion object {
@JvmStatic // accessed reflectively from core
fun getDispatcher(): MainCoroutineDispatcher = Main
}
override fun createDispatcher(): MainCoroutineDispatcher = Main
override val loadPriority: Int get() = Int.MAX_VALUE
}
To create different main dispatcher, Kotlin uses ServiceLoader to load MainDispatcherFactory, which iterates through the known and available providers, returns a suitable encoder. On JVM it either Android main thread dispatcher, JavaFx, or Swing EDT dispatcher. It is chosen by ServiceLoader.
private object MainDispatcherLoader {
@JvmField
val dispatcher: MainCoroutineDispatcher =
MainDispatcherFactory::class.java.let { clz ->
ServiceLoader.load(clz, clz.classLoader).toList()
}.maxBy { it.loadPriority }?.tryCreateDispatcher() ?:
MissingMainCoroutineDispatcher(null) private fun MainDispatcherFactory.tryCreateDispatcher(): =
try { createDispatcher() }
catch (cause: Throwable) {
MissingMainCoroutineDispatcher(cause)
}
}
So in the android project, we have to
implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:[version]
which offers resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory and tell ServiceLoader that the only one provider is AndroidDispatcherFactory
.
AndroidDispatcherFactory will create HandlerDispatcher. Its Main handler dispatcher will communicate with Looper.getMainLooper().
val mainHandler = Looper.getMainLooper().asHandler(async = true) val Main: HandlerDispatcher = HandlerContext(mainHandler, “Main”)
* Thanks my colleague, Ansgar, for sharing the information and know-how about Kotlin’s source code and Java.