[despite using init] Android: Stacktrace-decoroutinator runtime can not be loaded because BaseContinuationImpl was already loaded.
I've got this IllegalStateException although I'm using DecoroutinatorRuntime.load() in the init { } block of my Application class. In order to rule out that it is a general problem, I used an Android Studio template and created a simple sample app, added a custom MyApplication to the manifest and confirmed that DecoroutinatorRuntime.load() is not throwing.
To compare the initialization process of the two apps (the working and the not working one) I replaced the call to DecoroutinatorRuntime.load() with error("Error message"). It looks like it's exactly the same:
I noticed that the callstacks then load pretty identical:
Simple Sample App (Decoroutinator working)
Process: com.example.myapplication, PID: 21334
java.lang.RuntimeException: Unable to instantiate application com.example.myapplication.MyApplication package com.example.myapplication:
java.lang.IllegalStateException: Error message
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1466)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1395)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6959)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2236)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: java.lang.IllegalStateException: Error message
at com.example.myapplication.MyApplication.<init>(MyApplication.kt:8)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateApplication(AppComponentFactory.java:76)
at androidx.core.app.CoreComponentFactory.instantiateApplication(CoreComponentFactory.java:52)
at android.app.Instrumentation.newApplication(Instrumentation.java:1282)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1458)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1395)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6959)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2236)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
my real app (Decoroutinator not working)
Process: de.company.android.component.alpha, PID: 26890
java.lang.RuntimeException: Unable to instantiate application de.company.android.component.MyApplication package de.check24.android.homes.alpha:
java.lang.IllegalStateException: Error message
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1466)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1395)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6959)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2236)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: java.lang.IllegalStateException: Error message
at de.company.android.component.MyApplication.<init>(MyApplication.kt:18)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateApplication(AppComponentFactory.java:76)
at androidx.core.app.CoreComponentFactory.instantiateApplication(CoreComponentFactory.java:51)
at android.app.Instrumentation.newApplication(Instrumentation.java:1282)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1458)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1395)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6959)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2236)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Any ideas how I can find out what is causing this or how to fix this? I could imagine that some part of the app is doing some static initialization and is using Coroutines there. But since it is a huge codebase, I don't have a clue how I could start investigating on that -- exchanging the ClassLoader and setting a breakpoint has the same issue: it's already too late.
@cee-dee Maybe you can add a breakpoint on the constructor of the class kotlin.coroutines.jvm.internal.BaseContinuationImpl to try to figure out when this class loads initially.
Thank's for the awesome speed of your support! I've already tried to set breakpoints, but either no breakpoint actually breaks, or it never reaches the init block of my App.
This setup with emulated breakpoints (basically) never reaches the init block:
Do you have a concrete suggestion of where I should set breakpoints?
Is there anything else I can try to find the cause?
@cee-dee Just released SD version 2.4.1 with a big rework for Android. From now on SD for Android requires only the Gradle plugin and no other dependencies or method calls are required.
@Anamorphosee Sorry for getting back to you late, I now tried it: the application starts up, but I don't see the real callstack on a crash -- I tried your usage example and got this:
java.lang.Exception: exception at 1729242220513
at com.example.app.Test.rec(TopDestinationsViewModelDelegateImpl.kt:120)
at com.example.app.Test$rec$1.invokeSuspend(Unknown Source:15)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownSpecMethodsFactory$Spec.resumeNext(unknown.kt:83)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:28)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.UnknownKt.unknown(unknown.kt:20)
at dev.reformator.stacktracedecoroutinator.common.internal.AwakenerKt.callSpecMethods(awakener.kt:80)
at dev.reformator.stacktracedecoroutinator.common.internal.AwakenerKt.awake(awakener.kt:32)
at dev.reformator.stacktracedecoroutinator.common.internal.Provider.awakeBaseContinuation(provider-impl.kt:43)
at dev.reformator.stacktracedecoroutinator.provider.DecoroutinatorProviderApiKt.awakeBaseContinuation(provider-api.kt:47)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Unknown Source:20)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:101)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@b93d395, Dispatchers.Main.immediate]
Hi, @cee-dee.
Could you please show the output of method DecoroutinatorCommonApi.getStatus { it() } to check if Decoroutinator has been installed properly?
And did you try to call cradle clean after adding Decoroutinator Gradle plugin?
And do you use the latest version(2.4.5) of the plugin?
I used id("dev.reformator.stacktracedecoroutinator") version "2.4.5" in my App gradle module as described.
I did a Gradle Sync and a clean build, also issued gradle clean to make sure.
In order for calling DecoroutinatorCommonApi.getStatus { it() } I had to apply the Plugin in another build.gradle. (one module provides a library -- there I needed to add it -- and the other just runs the library code in a container application -- there I previously tried to apply the Plugin)
During compilation (of the code where I added the Plugin to the library code) I now get this error:
com.android.tools.r8.internal.jf: Multiple annotations of type `kotlin.coroutines.jvm.internal.DebugMetadata`
I've added
-keep @kotlin.coroutines.jvm.internal.DebugMetadata class * { *; }
-keep @dev.reformator.stacktracedecoroutinator.provider.DecoroutinatorTransformed class * { *; }
to my ProGuard configuration.
Am I missing something?
During compilation (of the code where I added the Plugin to the library code) I now get this error: com.android.tools.r8.internal.jf: Multiple annotations of type
kotlin.coroutines.jvm.internal.DebugMetadata
Seems interesting. Could you please delete the ProGuard rule -keep @kotlin.coroutines.jvm.internal.DebugMetadata class * { *; }?
And Decoroutinator plugin should be applied to the app build.gradle only. It does class transformation and adds decoroutinator-common dependency with the method DecoroutinatorCommonApi.getStatus { it() } automatically.
Ok.
Now, I only added
-keep @dev.reformator.stacktracedecoroutinator.provider.DecoroutinatorTransformed class * { *; }
to the ProGuard rules.
My build.gradle of the App (containing the Application class) now contains (as the only build.gradle file)
plugins {
id("dev.reformator.stacktracedecoroutinator") version "2.4.5"
}
My Application class contains
override fun onCreate() {
super.onCreate()
val status = DecoroutinatorCommonApi.getStatus { it() }
logger { "---------------------------- $status" }
which outputs
---------------------------- DecoroutinatorStatus(successful=true, description=no issues detected)
In the library Gradle module's code I added your sample code in a ViewModel:
viewModelScope.launch {
Test.rec(10)
}
...
object Test {
suspend fun rec(depth: Int) {
if (depth == 0) {
yield()
throw Exception("exception at ${System.currentTimeMillis()}")
}
rec(depth - 1)
}
}
I did a gradle clean again. But still, I get the callstack from above.
Are call of DecoroutinatorCommonApi.getStatus { it() } and declaration of object Test contained in the same sourceSet? For example, getStatus could be called in src/main and object Test in tests.
If so, please check get output of getStatus in the source set of object Test ?
No, they aren't. The structure of the project basically is like that: a) one Gradle module for a library which contains all of the app functionality b) one Gradle module for a wrapper app which embeds the library from a)
In the latest version, I included Decoroutinator into the module from b), but implemented and called the object Test inside of module from a) -- I mostly need to resolve the callstacks in the library.
So, from what I get from your last message, I cannot achieve it as you asked me to include the Gradle Plugin to the App module (which is the module from b). The library does not know anything of the wrapper app.
Ok than you should
- apply Decourutinator Gradle plugin to module
b - add dependency
dev.reformator.stacktracedecoroutinator:stacktrace-decoroutinator-common:2.4.5to modulea - make sure that calls of
getStatusand your test are in the same source sets. And check the output ofgetStatus
And 1 more question: how does module a include in module b?
I include module a in module b with
implementation(project(":module_a")) { transitive = false }
I set up everything as you described, and made a clean build. Now I get that output:
---------------------------- DecoroutinatorStatus(successful=false, description=the stack trace contains unknown frames. Probably some local source is not transformed)
it means that classes of module a don't transform for some reason. I tried but couldn't reproduce the issue.
So far you can fix it by allowing transformation at runtime. To do it you should add configuration below to your cradle.build
stacktraceDecoroutinator {
addAndroidRuntimeDependency = true
}
And it will be great if you could provide a minimal example of your project configuration reproducing the issue.
I added
stacktraceDecoroutinator {
addAndroidRuntimeDependency = true
}
to the module b build.gradle and everything is working fine, the status is
DecoroutinatorStatus(successful=true, description=no issues detected)
Also, I see the expected callstack :-) Thank you very much for your helpful and quick support! 👍
Do you think, the library is ready for production usage? Even with addAndroidRuntimeDependency = true? (What does it do exactly?) Or should I stick to using it in debug builds only?
Regarding the minimal example of the project configuration: that will probably be difficult since I'm working on a huge eco-system. I can try to get permission for a screensharing session though, if you think it will help.
Do you think, the library is ready for production usage? Even with addAndroidRuntimeDependency = true?
Yes, it's production ready.
What does it do exactly?
To recovery the stack trace classes have to contain auxiliary methods which are generated by Decorutinator Gradle plugin at build time. For some reason your project's classes are not transformed and don't contain that methods. That's why you saw UnknownKt instead of recovered frames.
Property addAndroidRuntimeDependency = true adds the dependency stacktrace-decoroutinator-generator-android which allows to generate that auxiliary methods at runtime.
So the resulting build will be smaller when addAndroidRuntimeDependency=true is used, right?
Do you generate the methods at build time to reduce app startup time, or what was the reason for the default?
So the resulting build will be smaller when addAndroidRuntimeDependency=true is used, right?
The resulting APK is smaller when addAndroidRuntimeDependency=false(which is default).
Do you generate the methods at build time to reduce app startup time, or what was the reason for the default?
Partly. The main function which done at build time is replacing the class BaseContinuationImpl from Kotlin stdlib. And it works for your project. Without it you wouldn't see any effect at all. And that action is mandatory and can be done only at build time.
And the second part is to generate the auxiliary methods. And it can be done both at build time or at run time. addAndroidRuntimeDependency=true turn it on at run time.
@cee-dee I have an hypothesis why your projects classes are not transformed at build time. Could you please add configuration below to your build.gradle.kts
stacktraceDecoroutinator {
artifactTypes = setOf(
"jar", "java-classes-directory", "zip", "aar", "android-classes-jar", "android-classes-directory"
)
}
and delete addAndroidRuntimeDependency = true and check if Decoroutinator works properly
Thanks for the update! Unfortunately, this doesn't work, it yields the "Unknown" callstack entries. The status is:
DecoroutinatorStatus(successful=false, description=the stack trace contains unknown frames. Probably some local source is not transformed)
I've asked for permission to do a screensharing session with you, though -- in case you'd be interested.