I recently enabled strict shrinker in my Android project to aggressively remove unused resources as described in the official documentation here https://developer.android.com/build/shrink-code#strict-reference-checks
However, after enabling it, my app started crashing whenever I tried to open the screen containing MotionLayout, with the following error:
Caused by: android.content.res.Resources$NotFoundException: Unable to find resource ID #0x7f0a03f0
at android.content.res.ResourcesImpl.getResourceTypeName(ResourcesImpl.java:316)
at androidx.constraintlayout.motion.widget.MotionScene$Transition.fill(MotionScene.java:978)
at androidx.constraintlayout.motion.widget.MotionScene.load(MotionScene.java:1090)
at androidx.constraintlayout.motion.widget.MotionLayout.init(MotionLayout.java:3840)
at androidx.constraintlayout.motion.widget.MotionLayout.<init>(MotionLayout.java:1124)
at com.mypackage.MyViewBinding.inflate(MyViewBinding.java:102)
at com.mypackage.MyView.<init>(MyView.kt:37)
The crash occurs because MotionLayout relies on reflection to handle its transitions and keyframes, which leads to issues when using the strict shrinker. Specifically, MotionLayout
uses reflection to initialize keyframes in its internal KeyFrames.java
file:
sKeyMakers.put(KeyAttributes.NAME, KeyAttributes.class.getConstructor())
sKeyMakers.put(KeyPosition.NAME, KeyPosition.class.getConstructor())
sKeyMakers.put(KeyCycle.NAME, KeyCycle.class.getConstructor())
sKeyMakers.put(KeyTimeCycle.NAME, KeyTimeCycle.class.getConstructor())
sKeyMakers.put(KeyTrigger.NAME, KeyTrigger.class.getConstructor())
Because these resource IDs are not explicitly referenced in the code, the strict shrinker considers them unused and removes them. This results in a Resources$NotFoundException
when MotionLayout tries to access these resources at runtime.
Solution
To prevent the shrinker from removing the necessary resource IDs, create a keep.xml file and specify the IDs of ConstraintSets used in your MotionLayout scenes with the tools:keep attribute. For example:
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@+id/set1,@+id/set2,@+id/set3"
tools:shrinkMode="strict" />
From the scene like:
<MotionScene
...
<ConstraintSet
android:id="@+id/set1">
<ConstraintSet
android:id="@+id/set2"
motion:deriveConstraintsFrom="@+id/set1">
<ConstraintSet
android:id="@+id/set3"
motion:deriveConstraintsFrom="@+id/set2">
</MotionScene>
Place this file in your res directory. It ensures that all specified resources remain in the final build and are available to MotionLayout.
Additionally, there is a Github issue about incompatibility between MotionLayout and R8 mode If you encounter some problems with it, give a try adding a line to your proguard configuration
-keepclassmembers class * extends androidx.constraintlayout.motion.widget.Key {
public <init>();
}