Open viratshukla opened 2 years ago
Please find sample code snippets.
@Composable fun UseReflection() { val packageName = "com.example.composeplayground.dsLibrary.MyComposeWidgets" val functionName = "Widget1"
val c = Class.forName("${packageName}Kt")
c.declaredMethods.forEach { method ->
if (method.kotlinFunction?.name == functionName) {
InvokeMethod(method)
}
}
}
@Composable fun InvokeMethod(method : Method) { val dataToBeInvoked = arrayListOf<Any?>()
method.kotlinFunction?.parameters?.forEachIndexed { index, param ->
dataToBeInvoked.add(paramList1[index])
}
// Since compiler adds currentCompose in signature
dataToBeInvoked.add(currentComposer)
// Some random zeroes are also added during compile time, hence this logic
val javaParamCount = method.parameterCount
val numOfZeroesToBeAdded = javaParamCount - dataToBeInvoked.size
for (x in 1..numOfZeroesToBeAdded) {
dataToBeInvoked.add(0)
}
method.kotlinFunction?.call(*dataToBeInvoked.toTypedArray())
}
File 2 MyComposeWidgets.kt content
package com.example.composeplayground.dsLibrary
import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color
val paramList1 = arrayListOf
@Composable fun Widget1() { Text("Some Random Text - Widget 1") }
val paramList2 = arrayListOf
@Composable fun Widget2(a : Color) { Text("Some Random Text - Widget 2") }
If I comment-out Widget2 method, then reflection works great, but if its not commented, then reflection fails.
Similar issue happens, If I pass dp as parameter in a composable function.
'@Composable' functions are modified by compiler plugin, so their real signatures are different from the source code.
Could you clarify, what is the business goal to use reflection with Composable functions?
@akurasov I need to generate some generic code, so that on runtime, I can show correct UI based on response. Yes, I am aware, that on compile time, currentComposer and some integers are added. And this reflection is working fine in all the cases, except this Color param or Dp param in method signature.
Can you please let me know the solution, if possible?
Although, there is a workaround, that if I wrap this Color param with my data class, it works fine.
But question is, why this androidx.compose.ui.graphics.Color and androidx.compose.ui.unit.Dp are not working in the first place?
// Some random zeroes are also added during compile time, hence this logic
This is just not deterministic, since Compose compiler does not make any guarantees how many additional parameters it's adding to composable functions, where it's adding them, what is the type, and most importantly, what the values should be. So randomly adding zeroes as integers is inviting a really big trouble to begin with.
This is not a desktop-specific issue. I'd recommend either revisiting the approach to not use reflection at all, or to open a discussion on the Compose main tracker / #compose channel in Kotlin discord.
@kirill-grouchnikov Currently I am following this article. https://commonsware.com/blog/2020/05/29/reflection-composables.html
Please share the link of Compose main tracker, so that I can open an issue there as well.
That article already has a link to an existing tracker bug for official reflection support that you can follow
@kirill-grouchnikov Thanks, that I will check. But main issue is still - androidx.compose.ui.graphics.Color causing error during reflection.
Same code is working for other params, if I replace that param with some other param. Or wrap my Color property with another data class.
And again, you're trying to do something that is very explicitly not supported.
@viratshukla So basically you want to send request exactly at rendering time? Could you give some more details about your task?
I would rather update State in a "request" thread and use simple if's in the Composition code.
@akurasov
Hi,
Basically, I have created all my Composable components and I am writing a wrapper using reflection, which will decide in run-time, which component should be displayed.
Why not just use simple composable function, that will decide this based on some State?
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
Hi, I am using reflection to invoke a composable method in some file. If that file has any method, which takes androidx.compose.ui.graphics.Color as a parameter, reflection fails.
Please find complete stack trace for the same.
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.composeplayground, PID: 30706 kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Inconsistent number of parameters in the descriptor and Java reflection object: 3 != 1 Calling: public fun Widget2(a: androidx.compose.ui.graphics.Color): kotlin.Unit defined in com.example.composeplayground.dsLibrary[DeserializedSimpleFunctionDescriptor@ec84127] Parameter types: [long, interface androidx.compose.runtime.Composer, int]) Default: false at kotlin.reflect.jvm.internal.calls.InlineClassAwareCaller.(InlineClassAwareCaller.kt:102)
at kotlin.reflect.jvm.internal.calls.InlineClassAwareCallerKt.createInlineClassAwareCallerIfNeeded(InlineClassAwareCaller.kt:159)
at kotlin.reflect.jvm.internal.calls.InlineClassAwareCallerKt.createInlineClassAwareCallerIfNeeded$default(InlineClassAwareCaller.kt:149)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:89)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:61)
at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:63)
at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:61)
at kotlin.reflect.jvm.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:63)
at kotlin.reflect.jvm.ReflectJvmMapping.getKotlinFunction(ReflectJvmMapping.kt:122)
at com.example.composeplayground.MainActivityKt.UseReflection(MainActivity.kt:50)
at com.example.composeplayground.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:35)
at com.example.composeplayground.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:34)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.material.SurfaceKt$Surface$6.invoke(Surface.kt:267)
at androidx.compose.material.SurfaceKt$Surface$6.invoke(Surface.kt:254)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)
at androidx.compose.material.SurfaceKt.Surface-F-jzlyU(Surface.kt:251)
at androidx.compose.material.SurfaceKt.Surface-F-jzlyU(Surface.kt:110)
at com.example.composeplayground.ComposableSingletons$MainActivityKt$lambda-2$1.invoke(MainActivity.kt:34)
at com.example.composeplayground.ComposableSingletons$MainActivityKt$lambda-2$1.invoke(MainActivity.kt:32)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)
at androidx.compose.material.TextKt.ProvideTextStyle(Text.kt:252)
at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:81)
at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:80)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)
at androidx.compose.material.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:72)
at com.example.composeplayground.ui.theme.ThemeKt.ComposePlaygroundTheme(Theme.kt:41)
at com.example.composeplayground.ComposableSingletons$MainActivityKt$lambda-3$1.invoke(MainActivity.kt:32)
at com.example.composeplayground.ComposableSingletons$MainActivityKt$lambda-3$1.invoke(MainActivity.kt:31)
E/AndroidRuntime: at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:384)
at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:228)
at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:227)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)
at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:148)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:114)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:113)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:106)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:162)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:161)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:161)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:144)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.ComposerKt.invokeComposable(Composer.kt:3330)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2577)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2573)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:540)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2566)
at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:2517)
at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:476)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:727)
at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:432)
at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:144)
at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:135)
at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:727)
at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:135)
at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:187)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:196)
at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:142)
E/AndroidRuntime: at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:135)
at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:814)
at android.view.View.dispatchAttachedToWindow(View.java:18347)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3397)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3404)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3404)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3404)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3404)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1761)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1460)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7183)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
at android.view.Choreographer.doCallbacks(Choreographer.java:761)
at android.view.Choreographer.doFrame(Choreographer.java:696)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
I/Process: Sending signal. PID: 30706 SIG: 9