https://kotlinlang.org logo
#compose
Title
# compose
c

Colton Idle

12/20/2020, 4:08 AM
I got compose to work in one existing project of mine. Now I'm trying to add it to another and I'm getting NoSuchMethodError: No static method setContent Any ideas? I have
Copy code
buildFeatures {
compose = true
}

composeOptions {
kotlinCompilerVersion '1.4.21'
kotlinCompilerExtensionVersion '1.0.0-alpha09'
}
kotlinOptions with useIR = true and jvmTarget set to 1.8 What else could be going wrong? I'm getting a crash at runtime. Everything in the IDE (latest canary) looks fine. Gist: Latest everything. No compile issues. Runtime issue when just hitting
setContent
and nothing else. Edit: Only other thing I can think of is that I use some dependency that is forcing an older version of kotlin? I tried to use ./gradlew dependencies to figure out, but everything looks like its 1.4.21 Another edit: There literally has to be a better way to get a better error message. Literally have no idea whats going on 😭 I already have been able to get compose working in past projects. I quadruple checked that I followed these steps correctly https://developer.android.com/jetpack/compose/setup#add-compose (except that I'm using kotlin 1.4.21, alpha09 of compose, and Canary 3 of AS. Could really use any bump in the right direction. Thanks!
j

Javier

12/20/2020, 9:06 AM
Maybe missing dependency? I don't remember where is
setContent
, I think it is in the
ui
artifact
Are you using AGP 7 Alpha 03? I think you need jdk 11+
j

jim

12/20/2020, 9:33 AM
If the code compiles, that would seem to indicate it is NOT a kotlin version incompatibility. Probably related to dependencies, but not merely a missing dependency, as the code compiles and it doesn't complain about class not found. Without more information, it's a bit hard to tell, but conceivable that somehow you're getting an older version of the dependency (via a transitive dependency) at runtime or a stripped down version of the class (via treeshaking) without that method. Gradle sometimes makes it hard to understand/debug such dependency resolution issues. Are you using any sort of tree shaker like R8 or Proguard? If so, can you turn those off? If you can dump the bytecode (using
javap -v
) for the class containing your
setContent
call and paste it here, that might help to narrow down . Specifically, I would be looking for the bytecode signature of the
setContent
call as invoked in your bytecode. Also, use reflection at runtime (where your setContent call would have been) to print the info (method names, method parameter types, method return values) about all methods on your
WrapperKt
class. You may spot the problem while doing this, or at the very least it will help point us in the right direction.
c

Colton Idle

12/20/2020, 3:42 PM
1. @Javier I have the compose runtime, ui, ui tooling, foundation and material all declared in my apps deps as alpha09 2. I am using AGP 7 alpha 3 3. @jim thanks for chiming in. I feel really confident that I know how to set compose up now (since I struggled for like 3 months getting that kotlin reflection bug I really got to narrow down the artifacts I need in an app to simply call setContent{}) so thank you for responding here as I'm pulling my hair out. a) "if the code compiles" Yep! It all runs fine until I try to load an activity or fragment with setContent. b) "probably related to dependencies" Yep. Thats where my head went to late last night. I started declaring a bunch of kotlin dependencies in my app level build.gradle in order to try to force newer dependency to override any old ones that may be picked up transitively. c) Yes, it sucks that gradle doesn't make this straightforward. d) no tree shaking (r8 or proguard) as these are all debug builds, but I checked again just to make sure e) I will try to dump the bytecode! Never done that before so give me a few minutes.
@jim I ran this command (not sure if it's right)
javap -v rt-app/build/tmp/kotlin-classes/bigideasStandardDebug/com/rollertoaster/app/userfacing/MainActivity.class
and the above is the result. ^^^ I'm going to try to run the command on an app that I have working with compose and see if I find anything.
Re: "Use reflection at runtime to print out info about WrapperKt" I've admittedly never had to use reflection in my life. But from what I read I would just call
WrapperKt::class.declaredMemberProperties
? If so, then maybe that's part of my problem because writing out WrapperKt::class underlines WrapperKt as unresolved reference. If I do a search in android studio in "All Places" for WrapperKt I get two hits. WrapperKt.class in
Copy code
/Users/coltonidle/.gradle/caches/transforms-3/6aecffe2d086391121cf64b1fda3073c/transformed/jetified-ui-1.0.0-alpha09/jars/classes.jar!/androidx/compose/ui/platform/WrapperKt.class
and Wrapper.kt in
Copy code
/Users/coltonidle/.gradle/caches/modules-2/files-2.1/androidx.compose.ui/ui/1.0.0-alpha09/2dab184f5cebeb6dc4856de8f4c0dc63b02900b8/ui-1.0.0-alpha09-sources.jar!/androidx/compose/ui/platform/Wrapper.kt
j

jim

12/20/2020, 11:10 PM
Kotlin reflection might be too clever by half. I'd try using Java reflection in this case, just
Class.forName("androidx.compose.ui.platform.WrapperKt")
to get the Java class object and grab the methods off that.
c

Colton Idle

12/20/2020, 11:14 PM
@jim trying now! Never used reflection so I had no idea. 😃 Does the bytecode dump tell you anything?
Not looking good @jim I just did that reflection trick to print the methods on my broken project and on my working project and they are identical. 😭
j

jim

12/20/2020, 11:28 PM
The third parameter of the
setContent$default
function in your bytecode is of type
Function0
and the matching functions in the class require the third parameter to be of type
Function2
- which at least explains the
NoSuchMethodError
. This means the problem is not a result of tree shaking. I think this means the transform is not running (due to either configuration issue or bug) on your
MainActivity.kt
. Still digging, give me a couple minutes.
c

Colton Idle

12/20/2020, 11:32 PM
Yeah. In the project that's working and the project that isn't working, the byte code looks different. Reflection printing of method names are identical between the two projects.
j

jim

12/20/2020, 11:39 PM
Can you add the following to the root of your
MainActivity.kt
file:
Copy code
fun foo() { bar() }
@Composable fun bar() {}
And then try to compile. If the plugin is installed, the compiler frontend should complain and give you an error when compiling
foo()
and refuse to perform the build.
c

Colton Idle

12/20/2020, 11:39 PM
In terms of misconfiguration the only thing that maybe comes to mind is the fact that my broken project uses buildSrc? But everything still compiles which is weird and all of my build.gradle files are the same.
Will try right now!
j

jim

12/20/2020, 11:42 PM
That is the IDE; does it say the same thing if you try to compile using the command line?
c

Colton Idle

12/20/2020, 11:43 PM
Ooh. Didn't try the command line. Also, I tried the same thing in my working project and it gave me the same error in the IDE.
j

jim

12/20/2020, 11:44 PM
(specifically, it is possible for the IDE to report errors because it is running the Compose Compiler while the command line could be doing something different, that's the first thing I want to rule out)
👍 1
c

Colton Idle

12/20/2020, 11:45 PM
Working project had this error when running from cmd line:
Copy code
e: MyActivity.kt: (19, 5): Functions which invoke @Composable functions must be marked with the @Composable annotation
e: MyActivity.kt: (20, 5): @Composable invocations can only happen from the context of a @Composable function
Broken project... compiled fine. 🤯
mind blown 1
j

jim

12/20/2020, 11:52 PM
Ok, so if the broken project compiled fine, then it does indeed appear the problem is that the Compose Compiler plugin is somehow not running.
Just because I'm paranoid and I didn't ask you to do this already before, can you run
jps -m
and then kill anything that looks like a Gradle daemon or a Kotlin daemon?
c

Colton Idle

12/20/2020, 11:53 PM
It's unlike (actually, impossible) that I'll be able to share this project for this client, but please let me know if there's anything I can do to help. Will run that command now.
j

jim

12/20/2020, 11:53 PM
For example, if
jps -m
returns:
Copy code
11685 GradleDaemon 6.8-rc-1
12138 KotlinCompileDaemon --daemon-runFilesPath /Users/jsproch/Library/Application Support/kotlin/daemon --daemon-autoshutdownIdleSeconds=7200 --daemon-compilerClasspath /Users/jsproch/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.4.20/fc1d26586910b32d676480c75acd3e922e5e81fa/kotlin-compiler-embeddable-1.4.20.jar:/Users/jsproch/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.4.20/411fc46e908bfa9c034f52b0d31b2e1f61f06127/kotlin-reflect-1.4.20.jar:/Users/jsproch/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/Users/jsproch/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.20/9793d2f6b262847a2d8127951c5786cf907cc7b1/kotlin-script-runtime-1.4.20.jar:/Users/jsproch/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-daemon-embeddable/1.4.20/a051291fb01bf2397759625626fec670cd57b3f0/kotlin-daemo
Then you would do a
kill -9 11685
and
kill -9 12138
c

Colton Idle

12/20/2020, 11:58 PM
I have 5 pids that show up. 1 KotlinCOmpileDaemon 2 Jps -m 3 GradleDaemon 6.8-rc-3 4 NO NAME!!! 5 NO NAME!!! Going to kill 1 and 2 for now. Let me know if I should try to kill 4 and 5.
j

jim

12/20/2020, 11:58 PM
Yes, please kill 4 and 5 also
c

Colton Idle

12/20/2020, 11:59 PM
Killing 4 just killed android studio. 😃
j

jim

12/20/2020, 11:59 PM
I was about to warn you that might happen 😛
c

Colton Idle

12/21/2020, 12:00 AM
and 5 killed jetbrains toolbox
Should I restart Android Studio and build or just go into my terminal and try to build from there?
j

jim

12/21/2020, 12:00 AM
na, just try from the termainl
c

Colton Idle

12/21/2020, 12:01 AM
Alright. ./gradlew assembleRollerStandardBetaDebug it is!
j

jim

12/21/2020, 12:01 AM
when you do the build, adding the
--no-daemon
flag wouldn't hurt
./gradlew assembleRollerStandardBetaDebug --no-daemon
c

Colton Idle

12/21/2020, 12:01 AM
Got it.
Starting 🤞
build successful. 😢 I've never been so upset about a successful build.
j

jim

12/21/2020, 12:04 AM
Ok, well, the good news is that we didn't just spend a whole bunch of time debugging something that was just a result of a rogue daemon.
😬 1
We also narrowed it down pretty substantially. It's not related to a wrong ui/runtime dependency version, and not related to treeshaking. Looks like there is something wrong with the configuration, such that the Compose Compiler plugin is not being loaded/run is my best guess at this point. Since you're unable to share your project, you're probably more-or-less on your own to debug exactly what that might be, but that should at least send you in the right direction, and we figured out that you can use
./gradlew assembleRollerStandardBetaDebug --no-daemon
to somewhat rapidly test your configuration now (better than needing to deploy to an emulator and run the code for each test).
Hopefully the above was helpful; if you can think of anything else I can do to help, just let me know.
c

Colton Idle

12/21/2020, 12:09 AM
Thank you so much Jim! The only thing I can think of is whether or not my buildSrc is messing with things. Or somehow gradle build cache since we use gradle enterprise.
I will keep debugging for tonight and see where that takes me. Time to make some coffee!
j

jim

12/21/2020, 12:11 AM
both of those sound like reasonable things to consider. Try turning off or clearing your build cache, try doing destructive simplification of your build until it looks like your working project to discover what the differences might be.
👍 1
c

Colton Idle

12/21/2020, 12:17 AM
Just leaving notes of what I tried: Tried
./gradlew assembleRollerStandardBetaDebug --no-daemon --no-build-cache
and it still compiled fine.
@jim it works... and it's beautiful 😢
Such a stupid issue. The culprit was buildSrc. I still don't have a full solution, but basically in your typical root level build.gradle you would have
Copy code
dependencies {
        classpath 'com.android.tools.build:gradle:7.0.0-alpha03'
inside the dependencies block^^. Well my team hasn't used that dependencies block in root level build.gradle in like 2 years because we've been using buildSrc. In buildSrc dir we have a dependencies block and that dependencies block houses the agp plugin. For more info on how this looks like in practice see "Step 1: Setting up your project specific Gradle Plugin" https://quickbirdstudios.com/blog/gradle-kotlin-buildsrc-plugin-android/
Copy code
dependencies {
      /* Example Dependency */
    /* Depend on the android gradle plugin, since we want to access it in our plugin */
    implementation("com.android.tools.build:gradle:3.5.0")
We've used essentially that method for 2 years and never had a problem, but it looks like in this case, compose compiler doesn't know? You know what's funny, like 2 days ago when I was also banging my head against a wall I actually added
Copy code
dependencies {
        classpath 'com.android.tools.build:gradle:7.0.0-alpha03'
to my root build.gradle but even that does not fix it. To fix my issue I need to add
Copy code
dependencies {
        classpath 'com.android.tools.build:gradle:7.0.0-alpha03'
and remove the
Copy code
dependencies {

    implementation("com.android.tools.build:gradle:7.0.0-alpha03")
in my buildSrc build.grade.kts The only issue that exists is that I have other plugins in buildSrc that actually require
Copy code
dependencies {

    implementation("com.android.tools.build:gradle:7.0.0-alpha03")
to be defined. So yeah, I'm basically in a weird situation. I wonder if there's like a classpath type thing that I can put in my buildSrc. SOmething like this
Copy code
dependencies {

    classpath("com.android.tools.build:gradle:7.0.0-alpha03")
that would satisfy everything. But yeah. Let me know if you have any pointers (gradle is very much something that requires a phd to understand it seems), but that seems to be the root issue. Phew
Update: Yeah so due to a whole bunch of issues that arise if I simply remove the
impl...
from buildSrc/build.gradle.kts I need a solution to somehow keep that impl, but also to add the
classpath...
to root build.gradle and actually have it picked up. Let me know if you know of any gradle magic here. I've been reading a whole bunch of forum posts and SO posts but I also suck at gradle so maybe I'm missing something obvious. Anyway. Im going to bed. Thank you so much for the help @jim One step closer to my team using compose.
@jim do you think I should file a bug report for the agp, Gradle, AS, or compose team?
j

jim

12/21/2020, 5:47 PM
Hmm, yeah, maybe my intuition would be the AGP team?
They can always reroute if they determine another team is responsible.
c

Colton Idle

12/21/2020, 5:48 PM
No one from AGP team on here right? I haven't seen Xav around.
j

jim

12/21/2020, 5:53 PM
Not that I know of.
c

Colton Idle

12/21/2020, 7:13 PM
@jim got a super nice minimal repro. 🙏 Hopefully someone from agp chimes in on some workaround. https://issuetracker.google.com/issues/176079157
👍 1
4 Views