Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compose multiplatfrom and jetpack compose compatibility problem (java.lang.NoSuchMethodError MeasureScope#layout$default) #5020

Open
firelion9 opened this issue Jun 24, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@firelion9
Copy link

Describe the bug
Android application crashes after calling multiplatform composable function which itself calls MeasureScope#layout$default without passing alignmentLines parameter. The crash is reproducible when using compose multiplatform 1.6.11 and jetpack compose 1.6.7 (which are said to compatible (docs))

Affected platforms

  • Android
    The issue affects only android platform, but it seems that the problem is not in jetpack compose

Versions

  • Libraries:

    • Compose Multiplatform version: 1.6.11
    • Jetpack Compose version: 1.6.7
  • Kotlin version: 2.0.0

  • OS versions: Android 14 (both hardware and emulator)

To Reproduce
Simple project reproducing the issue (the stacktrace at the end was taken from this app): https://github.com/firelion9/compose-issue

Steps to reproduce the behavior:

  1. Write the following function in compose multiplatform project:
     @Composable
     fun JbBox(modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit = {}) {
         Box(
             modifier = modifier
                 .layout { measurable, constraints ->
                     val placeable = measurable.measure(constraints)
                     layout(placeable.width, placeable.height) { // <--- the problem rises here
                         placeable.place(0, 0)
                     }
                 },
             content = content
         )
     }
  2. Use it in in android app:
// in Activity#onCreate: 
// ...
setContent {
  JbBox {
      Text(text = "Everything is ok", fontSize = 24.sp)
  }
}
// ...
  1. Assemble the android app using ./gradlew assemble
  2. Install the app and launch the activity

Actual behavior
App crashes with the following stacktrace:

stacktrace
  java.lang.NoSuchMethodError: No static method layout$default(Landroidx/compose/ui/layout/MeasureScope;IILjava/util/Map;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Landroidx/compose/ui/layout/MeasureResult; in class Landroidx/compose/ui/layout/MeasureScope; or its super classes (declaration of 'androidx.compose.ui.layout.MeasureScope' appears in /data/app/~~LU3_khy9PGLqWFNT6qOyTw==/com.firelion.composeissue--IW9u4BB3tq5PIncLBCMPw==/base.apk)
      at com.firelion.composeissue.jb.JbBoxKt.JbBox$lambda$1(JbBox.kt:15)
      at com.firelion.composeissue.jb.JbBoxKt.$r8$lambda$Qhg8UchGWaQufvJNnzws3rlvJ3A(Unknown Source:0)
      at com.firelion.composeissue.jb.JbBoxKt$$ExternalSyntheticLambda1.invoke(D8$$SyntheticClass:0)
      at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)
      at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:252)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:251)
      at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2303)
      at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:500)
      at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:256)
      at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
      at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1617)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:36)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:596)
      at androidx.compose.ui.layout.RootMeasurePolicy.measure-3p2s80s(RootMeasurePolicy.kt:38)
      at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:252)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:251)
      at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2303)
      at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:500)
      at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:256)
      at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
      at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1617)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:36)
      at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
      at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.kt:1145)
      at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:354)
      at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureOnly(MeasureAndLayoutDelegate.kt:562)
      at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureOnly(MeasureAndLayoutDelegate.kt:407)
      at androidx.compose.ui.platform.AndroidComposeView.onMeasure(AndroidComposeView.android.kt:1058)
      at android.view.View.measure(View.java:27122)
      at androidx.compose.ui.platform.AbstractComposeView.internalOnMeasure$ui_release(ComposeView.android.kt:302)
      at androidx.compose.ui.platform.AbstractComposeView.onMeasure(ComposeView.android.kt:289)
      at android.view.View.measure(View.java:27122)
      at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7008)
      at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
      at android.view.View.measure(View.java:27122)
      at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7008)
      at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1608)
      at android.widget.LinearLayout.measureVertical(LinearLayout.java:878)
      at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
      at android.view.View.measure(View.java:27122)
      at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7008)
      at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
      at com.android.internal.policy.DecorView.onMeasure(DecorView.java:750)
      at android.view.View.measure(View.java:27122)
      at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:4182)
      at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2759)
      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3086)
      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2465)
      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9305)
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1339)
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
      at android.view.Choreographer.doCallbacks(Choreographer.java:952)
      at android.view.Choreographer.doFrame(Choreographer.java:882)
      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
      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)
  

Expected behavior
The activity launches and shows label "Everything is ok".

Additional context
The problem is not reproducible without using compose multiplatform (if you move JbBox to android module) and when you compile and run application using android studio's "run" action

@firelion9 firelion9 added bug Something isn't working submitted labels Jun 24, 2024
@kropp
Copy link
Member

kropp commented Jun 27, 2024

There are two overloads for layout on Android, and just a single one in Compose Multiplatform.
Judging from the exception, it seems that some multiplatform artifact is packed into an android app, while during the compilation the correct one is used.
Can you check the dependencies, please?
Unfortunately, I wasn't able to reproduce this issue on the sample project on my machine.

@kropp kropp removed the submitted label Jun 27, 2024
@firelion9
Copy link
Author

I checked my dependencies, everything looks fine: all compose mutiplatform dependencies are compile-only and androidx.compose.ui:ui-android:1.6.7 is presented in both compile and runtime classpaths of app module. I also checked MeasureScope definition in android and desktop variants of androidx.compose.ui:ui and they both contain single layout overload:

fun layout(
        width: Int,
        height: Int,
        alignmentLines: Map<AlignmentLine, Int> = emptyMap(),
        placementBlock: Placeable.PlacementScope.() -> Unit
    ): MeasureResult { /* ... */ }

Are you sure we are speaking about the same layout?

Unfortunately, I currently have access to a single windows machine only, but I tried to compile the sample project again from a clean checkout on it with the same result: successful run with android studio's run action and crash when compiling with ./gradlew :app:assembleDebug or with "generate signed app bundle / apk".

I also inspected generated apks and found out that in crashing apks required method is presented in MeasureScope$-CC instead of MeasureScope, but I still don't understand why it happens and why it doesn't happen with android studio's run action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants