-
Notifications
You must be signed in to change notification settings - Fork 660
Description
Describe the bug
When initializing a Json instance with a SerializersModule that includes a custom KSerializer, a class initialization deadlock can occur, leading to an ANR (Application Not Responding) on Android. This seems to happen specifically when the custom serializer's descriptor is initialized eagerly (without by lazy).
To Reproduce
Create a custom KSerializer for any type (e.g., java.time.Instant). Initialize its descriptor property directly.
object InstantSerializer : KSerializer<Instant> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)
// ...
}
Create a singleton object that configures a Json instance using this serializer.
object JsonHolder {
private val serializersModule: SerializersModule = SerializersModule {
contextual(Instant::class, InstantSerializer)
}
val baseJson: Json by lazy {
Json {
serializersModule = JsonHolder.serializersModule
explicitNulls = false
ignoreUnknownKeys = true
encodeDefaults = true
coerceInputValues = true
}
}
}
Access the JsonHolder singleton for the first time on the main thread during application startup, for example, as part of a Dagger dependency graph initialization.
main (runnable):tid=[TID] systid=[SYS_TID]
#00 pc [ADDRESS] libart.so (art::DumpNativeStack + [OFFSET]) (BuildId: [BUILD_ID])
#01 pc [ADDRESS] libart.so (art::Thread::DumpStack const + [OFFSET]) (BuildId: [BUILD_ID])
#02 pc [ADDRESS] libart.so (art::DumpCheckpoint::Run + [OFFSET]) (BuildId: [BUILD_ID])
#03 pc [ADDRESS] libart.so (art::Thread::RunCheckpointFunction + [OFFSET]) (BuildId: [BUILD_ID])
#04 pc [ADDRESS] libart.so (artTestSuspendFromCode + [OFFSET]) (BuildId: [BUILD_ID])
#05 pc [ADDRESS] libart.so (art_quick_test_suspend + [OFFSET]) (BuildId: [BUILD_ID])
at java.lang.String.fillBytesLatin1(Native method)
at java.lang.String.fillBytes(String.java:4400)
at java.lang.AbstractStringBuilder.putStringAt(AbstractStringBuilder.java:1693)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:549)
at java.lang.StringBuilder.append(StringBuilder.java:186)
at kotlinx.serialization.internal.PrimitiveArrayDescriptor.<init>(CollectionDescriptors.kt:114)
at kotlinx.serialization.internal.PrimitiveArraySerializer.<init>(CollectionSerializers.kt:147)
at kotlinx.serialization.internal.BooleanArraySerializer.<init>(PrimitiveArraysSerializers.kt:370)
at kotlinx.serialization.internal.BooleanArraySerializer.<clinit>(PrimitiveArraysSerializers.kt:12)
at kotlinx.serialization.builtins.BuiltinSerializersKt.BooleanArraySerializer(BuiltinSerializers.kt:174)
at kotlinx.serialization.internal.PlatformKt.initBuiltins(Platform.kt:188)
at kotlinx.serialization.internal.PrimitivesKt.<clinit>(Primitives.kt:18)
at kotlinx.serialization.descriptors.SerialDescriptorsKt.PrimitiveSerialDescriptor(SerialDescriptors.kt:91)
at com.mycompany.app.core.data.json.InstantSerializer.<clinit>(InstantSerializer.kt:18)
at com.mycompany.app.core.data.utils.JsonProvider.<clinit>(JsonProvider.kt:13)
at com.mycompany.app.core.data.di.module.CoreDataModule$JsonInner.provideJson(CoreDataModule.java:94)
at com.mycompany.app.core.data.di.module.CoreDataModule_JsonInner_ProvideJsonFactory.provideJson(CoreDataModule_JsonInner_ProvideJsonFactory.java:38)
at com.mycompany.app.core.data.di.DaggerCoreDataComponent$CoreDataComponentImpl.getBaseJson(DaggerCoreDataComponent.java:168)
at com.mycompany.app.core.network.di.DaggerCoreNetworkComponent$CoreNetworkComponentImpl.getBaseJsonRetrofitConverterFactory(DaggerCoreNetworkComponent.java:441)
at com.mycompany.app.feature.featureA.impl.di.DaggerRetrofitBinderComponent$RetrofitBinderComponentImpl.getRetrofit(DaggerRetrofitBinderComponent.java:55)
at com.mycompany.app.feature.featureA.impl.di.DaggerExperimentsFeatureComponent$ExperimentsFeatureComponentImpl$SwitchingProvider.get(DaggerExperimentsFeatureComponent.java:145)
at dagger.internal.DoubleCheck.getSynchronized(DoubleCheck.java:54)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:45)
at com.mycompany.app.feature.featureA.impl.di.DaggerExperimentsFeatureComponent$ExperimentsFeatureComponentImpl$SwitchingProvider.get(DaggerExperimentsFeatureComponent.java:142)
at dagger.internal.DoubleCheck.getSynchronized(DoubleCheck.java:54)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:45)
at com.mycompany.app.feature.featureA.impl.di.DaggerExperimentsFeatureComponent$ExperimentsFeatureComponentImpl$SwitchingProvider.get(DaggerExperimentsFeatureComponent.java:139)
at dagger.internal.DoubleCheck.getSynchronized(DoubleCheck.java:54)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:45)
at com.mycompany.app.feature.featureA.impl.di.DaggerExperimentsFeatureComponent$ExperimentsFeatureComponentImpl.getGenericExperimentsInteractor(DaggerExperimentsFeatureComponent.java:111)
at com.mycompany.app.feature.featureB.impl.di.DaggerFeatureToggleApiComponent$FeatureToggleApiComponentImpl$SwitchingProvider.get(DaggerFeatureToggleApiComponent.java:209)
at dagger.internal.DoubleCheck.getSynchronized(DoubleCheck.java:54)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:45)
Expected behavior
The Json object should be created and initialized without freezing the main thread or causing an ANR. The application should start up smoothly.
Environment
- Kotlin version: 2.1.21
- Library version: 1.7.3
- Platforms: Android
- Gradle version: 8.14