Add R8 keep rule for Hilt_* base classes to fix ClassCastException in release builds#5195
Add R8 keep rule for Hilt_* base classes to fix ClassCastException in release builds#5195Senthil455 wants to merge 1 commit into
Conversation
|
This seems dangerous, as it will prevent dead code elimination, or obfuscation of the generated code, which will then reveal the names of the actual component classes. We have r8 full mode enabled and do not hit this issue, and the original report didn't include enough information (what class is being cast to what class) to properly debug or fix this, but just enough for AI to over-eagerly produce a blind PR, which this very clearly is. |
… release builds Use -keep,allowobfuscation,allowshrinking instead of bare -keep so that R8 can still rename and remove unused Hilt_* classes while preventing vertical class merging that breaks the bytecode-transformed hierarchy. Fixes google#4668
ff03870 to
24c5002
Compare
|
@AlexanderGH Thanks for the review. For the record, this PR was not AI-generated. The initial version was incomplete, and your comments helped identify the gaps in the explanation and the keep rule. I've updated the PR to use The updated rule follows the same pattern already used by Dagger's |
I'm a bit confused why this would cause an issue with So even after the merge However, I also agree with @AlexanderGH that I'm surprised that this breaks, as I thought R8 should generally keep track of Perhaps the best place to start would be to add a simple repro example to Dagger's Gradle tests to first show the issue is reproducible for the case you expect. |
When using
@AndroidEntryPointon a class (e.g., a Service, Activity, or BroadcastReceiver) with R8 full mode enabled (android.enableR8.fullMode=true), the app crashes at runtime with:java.lang.RuntimeException: Unable to create service *.presentation.service.PushMessagingService: java.lang.ClassCastException
This works correctly in debug builds and when R8 full mode is disabled.
Root Cause:
In R8 full mode, the aggressive vertical class merging optimization merges
@AndroidEntryPointuser classes with their generatedHilt_*base classes when the user class has no additional methods. For example,PushMessagingService(class PushMessagingService : FirebaseMessagingService()) would be merged withHilt_PushMessagingServiceinto a single class.This merging breaks the bytecode-transformed class hierarchy set up by
AndroidEntryPointClassVisitor, which rewrites@AndroidEntryPointclasses to extendHilt_*instead of their original superclass. After merging, theinstanceof GeneratedComponentManagercheck inEntryPoints.get()fails because the flattened class hierarchy no longer implements the Hilt interface, causing theClassCastException.Fix:
Added
-keep,allowobfuscation,allowshrinking class **.Hilt_*to the Hilt Android proguard rules. This prevents R8 from merging the generatedHilt_*base classes with user classes via vertical class merging, while still allowing R8 to rename unused classes (allowobfuscation) and remove them when proven unused (allowshrinking).This follows the same pattern used by Dagger's
@LazyClassKey(LazyClassKeyProcessingStep.java:46).Changes
hilt-android/main/resources/META-INF/com.android.tools/r8/hilt-android.pro: Added keep rulehilt-android/main/resources/META-INF/com.android.tools/proguard/hilt-android.pro: Added keep ruleFixes ClassCastException (Unable to create service) in release builds #4668