Kotlin Coroutine Dispatcher Gets Android Main Dispatcher by ServiceLoader

Update on March 2, 2020

Bram Yeh
2 min readNov 23, 2018

After kotlinx.coroutines PR #1572, it will use Class.forName instead of ServiceLoader to instantiate Dispathcers.Main on Andriod.

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.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Bram Yeh
Bram Yeh

Written by Bram Yeh

Lead Android & iOS Mobile Engineer at Yahoo (Verizon Media) Taiwan https://www.linkedin.com/in/hanruyeh/

Responses (1)

Write a response