https://kotlinlang.org logo
#coroutines
Title
# coroutines
z

Zoltan Demant

02/28/2024, 9:18 AM
Ive specified
implementation(libs.coroutines.swing)
for my jvm/desktop target, but at runtime I run into issues relating to
Module with the Main dispatcher had failed to initialize
and it also mentions
Caused by: java.lang.NoClassDefFoundError: android/os/Looper
which seems odd given that its a desktop application. Any hints or ideas as to what might be wrong? Ive read about proguard for desktop potentially removing the dispatcher, but disabling proguard also does not seem to make a difference.
s

Sam

02/28/2024, 9:22 AM
Is there a stacktrace for that
NoClassDefFoundError
? It'd be interesting to see where it's coming from. I'm wondering if maybe the kotlinx-coroutines-android dependency has made its way onto the classpath from somewhere else.
z

Zoltan Demant

02/28/2024, 9:27 AM
Sure, the stack trace is enormous but I think this is the right bit? Definitely seeing Android stuff in there, not sure why though?
Copy code
Suppressed: java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used
		at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.missing(MainDispatchers.kt:111)
		at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.isDispatchNeeded(MainDispatchers.kt:92)
		at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:315)
		at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
		... 235 more
	Caused by: java.lang.NoClassDefFoundError: android/os/Looper
		at kotlinx.coroutines.android.AndroidDispatcherFactory.createDispatcher(HandlerDispatcher.kt:51)
		at kotlinx.coroutines.internal.MainDispatchersKt.tryCreateDispatcher(MainDispatchers.kt:53)
		at kotlinx.coroutines.internal.MainDispatcherLoader.loadMainDispatcher(MainDispatchers.kt:34)
		at kotlinx.coroutines.internal.MainDispatcherLoader.<clinit>(MainDispatchers.kt:18)
		at kotlinx.coroutines.Dispatchers.getMain(Dispatchers.kt:20)
		at androidx.compose.ui.text.platform.DispatcherKt.<clinit>(Dispatcher.kt:33)
		at androidx.compose.ui.text.font.FontListFontFamilyTypefaceAdapter.<init>(FontListFontFamilyTypefaceAdapter.kt:58)
		at androidx.compose.ui.text.font.FontListFontFamilyTypefaceAdapter.<init>(FontListFontFamilyTypefaceAdapter.kt:50)
		at androidx.compose.ui.text.font.FontFamilyResolverImpl.<init>(FontFamilyResolver.kt:31)
		at androidx.compose.ui.text.font.FontFamilyResolver_sikioKt.createFontFamilyResolver(FontFamilyResolver.skiko.kt:78)
		at androidx.compose.ui.text.platform.FontLoader.<init>(PlatformFont.skiko.kt:176)
		at androidx.compose.ui.node.RootNodeOwner$OwnerImpl.<init>(RootNodeOwner.skiko.kt:267)
		at androidx.compose.ui.node.RootNodeOwner.<init>(RootNodeOwner.skiko.kt:118)
		at androidx.compose.ui.scene.MultiLayerComposeSceneImpl.<init>(MultiLayerComposeScene.skiko.kt:114)
		at androidx.compose.ui.scene.MultiLayerComposeScene_skikoKt.MultiLayerComposeScene(MultiLayerComposeScene.skiko.kt:93)
		at androidx.compose.ui.scene.MultiLayerComposeScene_skikoKt.MultiLayerComposeScene$default(MultiLayerComposeScene.skiko.kt:86)
		at androidx.compose.ui.scene.ComposeContainer.createComposeScene(ComposeContainer.desktop.kt:248)
		at androidx.compose.ui.scene.ComposeContainer.access$createComposeScene(ComposeContainer.desktop.kt:67)
		at androidx.compose.ui.scene.ComposeContainer$mediator$3.invoke(ComposeContainer.desktop.kt:119)
		at androidx.compose.ui.scene.ComposeContainer$mediator$3.invoke(ComposeContainer.desktop.kt:119)
		at androidx.compose.ui.scene.ComposeSceneMediator$scene$2.invoke(ComposeSceneMediator.desktop.kt:193)
		at androidx.compose.ui.scene.ComposeSceneMediator$scene$2.invoke(ComposeSceneMediator.desktop.kt:193)
		at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
		at androidx.compose.ui.scene.ComposeSceneMediator.getScene(ComposeSceneMediator.desktop.kt:193)
		at androidx.compose.ui.scene.ComposeSceneMediator.setCompositionLocalContext(ComposeSceneMediator.desktop.kt:197)
		at androidx.compose.ui.scene.ComposeContainer.setCompositionLocalContext(ComposeContainer.desktop.kt:129)
		at androidx.compose.ui.awt.ComposeWindowPanel.setCompositionLocalContext(ComposeWindowPanel.desktop.kt:70)
		at androidx.compose.ui.awt.ComposeWindow.setCompositionLocalContext(ComposeWindow.desktop.kt:114)
		at androidx.compose.ui.window.Window_desktopKt$Window$10.invoke(Window.desktop.kt:411)
		at androidx.compose.ui.window.Window_desktopKt$Window$10.invoke(Window.desktop.kt:406)
		at androidx.compose.ui.window.AwtWindow_desktopKt$AwtWindow$2.invoke(AwtWindow.desktop.kt:70)
		at androidx.compose.ui.window.AwtWindow_desktopKt$AwtWindow$2.invoke(AwtWindow.desktop.kt:69)
		at androidx.compose.runtime.DisposableEffectImpl.onRemembered(Effects.kt:82)
		at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:1295)
		at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:984)
		at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:1005)
		at androidx.compose.runtime.Recomposer.composeInitial$runtime(Recomposer.kt:1099)
		at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:633)
		at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:619)
		at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2.invokeSuspend(Application.desktop.kt:219)
		... 15 more
	Caused by: java.lang.ClassNotFoundException: android.os.Looper
		at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
		at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
		at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
		... 55 more
Caused by: java.lang.NoClassDefFoundError: android/os/Build$VERSION
	at kotlinx.coroutines.android.AndroidExceptionPreHandler.handleException(AndroidExceptionPreHandler.kt:43)
	at kotlinx.coroutines.internal.CoroutineExceptionHandlerImpl_commonKt.handleUncaughtCoroutineException(CoroutineExceptionHandlerImpl.common.kt:34)
	... 244 more
Caused by: java.lang.ClassNotFoundException: android.os.Build$VERSION
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
	... 246 more
s

Sam

02/28/2024, 9:35 AM
That
kotlinx.coroutines.android.AndroidDispatcherFactory
part sure makes it look like
kotlinx-coroutines-android
is on the classpath
Can you use some Gradle magic to see how it's getting there? e.g.
./gradlew dependencyInsight --dependency=kotlinx-coroutines-android
z

Zoltan Demant

02/28/2024, 10:58 AM
What exactly am I looking for when using dependencyInsight? Since Im also targeting android, coroutines-android is included in many of my modules as well!
Or perhaps they should all be including -core?
ChatGPT: From the provided dependency insight, we can see that
kotlinx-coroutines-play-services:1.8.0
depends on the
kotlinx-coroutines-bom:1.8.0
, which includes
kotlinx-coroutines-android:1.8.0
. This BOM (Bill of Materials) file specifies versions for all kotlinx-coroutines libraries consistently to avoid version conflicts.
kotlinx-coroutines-play-services is included in ktor.
s

Sam

02/28/2024, 11:09 AM
🤔 not sure about that. A BOM normally doesn't actually add dependencies, it just manages their versions.
You should really only be seeing
coroutines-android
in the android-specific parts of your application. As you said,
coroutines-core
is all you need in common code.
z

Zoltan Demant

02/28/2024, 11:12 AM
org.jetbrains.kotlinxkotlinx coroutines android1.8.0 \--- org.jetbrains.kotlinxkotlinx coroutines bom1.8.0 +--- org.jetbrains.kotlinxkotlinx coroutines android1.8.0 (*) +--- org.jetbrains.kotlinxkotlinx coroutines jdk81.8.0 | +--- io.ktorktor client android jvm2.3.8 (requested org.jetbrains.kotlinxkotlinx coroutines jdk81.7.1) | | +--- io.ktorktor bom2.3.8 Does this look fishy?
s

Sam

02/28/2024, 11:14 AM
Are you adding an explicit dependency on ktor-client-android?
If so, don't 😄
z

Zoltan Demant

02/28/2024, 11:14 AM
Only in androidMain, shouldnt I? 😅 How else can I use the Android engine?
s

Sam

02/28/2024, 11:16 AM
Yeah that's fine in the android-specific code. It just shouldn't be present in the common/desktop stuff. I don't do much android/multiplatform development, so I'm not sure how to filter down the dependency insight report to make sure it's just showing the relevant bits.
z

Zoltan Demant

02/28/2024, 11:17 AM
Just for sanity, I tried removing it from androidMain and just running desktop - no change (as expected, but still good to know I guess 😄)
This just stopped working the other day, so its been fine for a few months.. unfortunately Im not sure what changed (only a portion of the desktop app fails due to this, so hard to notice)
But the underlying issue I take it is that somehow, Android (coroutines) gets pulled into desktop, and the crash is due to it then trying to find the main dispatcher from Android instead of desktop?
s

Sam

02/28/2024, 11:20 AM
Based on everything you've shared, it sure looks that way
z

Zoltan Demant

02/28/2024, 11:27 AM
The way I have things setup, my design system library has a
image
module with a dependency on
coil
(the KMP versions) and for jvmMain includes coroutines-swing. I can run that project just fine, and previously when I would pull it into my other project (the one thats failing now) it would work just fine. So Im guessing its something in this project specifically, but its a large project 😅
s

Sam

02/28/2024, 11:30 AM
It sounds a bit strange to have a dependency on
coroutines-swing
in a library. Typically you would only need to include that as a runtime dependency when you're actually deploying an application.
Same for
coroutines-android
.
z

Zoltan Demant

02/28/2024, 11:34 AM
Screenshot_20240228-123324.png
s

Sam

02/28/2024, 11:37 AM
Sure, but that doesn't mean you need it as a dependency of a library that has a transitive dependency on Coil. If it worked that way, they'd have added it as a dependency to Coil itself. All you need is to make sure the dispatcher is present at runtime.
I guess making different versions of your library for different platforms will work, but it seems like an easy way to accidentally get the wrong dependencies in the wrong places
z

Zoltan Demant

02/28/2024, 11:40 AM
All you need is to make sure the dispatcher is present at runtime.
Isnt that what Im doing by specifying it as a dependency in my library? Fwiw, both projects are KMP based.
Ive also noticed that I can see different variants of coroutines in the
dependencyInsight
; both core, jvm, etc, are present (along with android).
s

Sam

02/28/2024, 11:47 AM
That's not necessarily a problem—afaik -core-jvm is a variant of -core, but is a separate library from -android
z

Zoltan Demant

02/28/2024, 12:32 PM
I just meant that it should then be fine that Im seeing coroutines-android as part of the dependencyInsights as well?
Oh no.. I made a terrible mistake 🤦🏽‍♂️
Perhaps a good feature request? I added android-workmanager to commonMain instead of androidMain 😅
I guess since android <> jvm, kind of, I dont get an error message because of it
Thanks for all the help, definitely learned a few cool new things (dependencyInsight 😮) from our discussion, I just wish we werent chasing ghosts the whole time 😄
10 Views