Crash when Android-specific classes are used as top-level val
Closed this issue · 5 comments
We are trying to use ComposablePreviewScanner with Paparazzi.
We have some components that use Android-specific classes in it, and the scanner crashes when scanning these classes.
Example 1
private val SomePath = android.graphics.Path().apply { ... }
The scanner crashes with
java.lang.UnsatisfiedLinkError: 'long android.graphics.Path.nInit()'
at android.graphics.Path.nInit(Native Method)
at android.graphics.Path.<init>(Path.java:51)
at mypackage.SomeComponentKt.<clinit>
Example 2
private val Typeface = android.graphics.Typeface.create("sans-serif-medium", Typeface.NORMAL)
The scanner crashes with
java.lang.NullPointerException: Cannot invoke "java.util.Map.get(Object)" because "android.graphics.Typeface.sSystemFontMap" is null
at android.graphics.Typeface.getSystemDefaultTypeface(Typeface.java:1216)
at android.graphics.Typeface.create_Original(Typeface.java:899)
at android.graphics.Typeface_Delegate.create(Typeface_Delegate.java:109)
at android.graphics.Typeface.create(Typeface.java:899)
at mypackage.SomeComponentKt.<clinit>
After Paparazzi is initialized (when using Paparazzi
as the test rule), the crash doesn't happen, but the preview list need to be generated before the test rule kicks in.
We can use something like lazy
as a workaround, but I'm wondering if there's some other way that doesn't require code change.
@mxalbert1996
So I can reproduce it, and this happens only with Paparazzi: with Roborazzi this is not reproducible. I think the best to avoid this is to use some lazy initialization as you mentioned, or just make those vals compute those values every time
private val SomePath
get() = android.graphics.Path()
instead of this
private val SomePath = android.graphics.Path()
Another option would be to put your @Previews
inside a class, which is supported since ComposablePreviewScanner 0.3.0.
Just let me know if it helps
I believe it might work without changing those global vals if you use ParameterizedRunner instead of the TestParameterInjector.
How to set it up is explained in the Roborazzi section. The setup the same, but use ParameterizedTestRunner instead of RobolectricParameterizedTest.
So only the test code would need changes
I’ll give it a try tonight and report back
I believe it might work without changing those global vals if you use ParameterizedRunner instead of the TestParameterInjector.
How to set it up is explained in the Roborazzi section. The setup the same, but use ParameterizedTestRunner instead of RobolectricParameterizedTest.
So only the test code would need changes
I’ll give it a try tonight and report back
Looks like using Parameterized instead of TestParameterInjector makes no difference.
The issue seems to be related to the way Paparazzi download android resources, which seems to interfere somehow with ComposablePreviewScanner, so my best bet is that you'll have to resort to one of the approaches I mentioned previously
@mxalbert1996
After checking ComposablePreviewScanner code I’ve understood where the problem lies (has to do with Class.forName()) and I have a couple of ideas on how to solve it without you needing to change the problematic vals that access android classes.
I’ll try to publish a new version ComposablePreviewScanner 0.3.1 with a fix for this issue next week
@mxalbert1996 should be fixed in 0.3.2 😊