tezvyn:

Write the ProGuard keep rule to fix reflection after R8

AI-drafted, machine-checkedSource: developer.android.comadvanced
Write the ProGuard keep rule to fix reflection after R8

This tests R8 dead-code removal and exact ProGuard syntax for reflective constructors. A strong answer gives -keep class com.example.T { <init>(...); } because R8 cannot trace dynamic class names. Broad -keep rules or omitting constructor are red flags.

WHAT THIS TESTS: This question probes whether you understand R8 whole-program optimization and the boundary between static analysis and runtime reflection. The interviewer cares if you know that R8 builds a call graph to delete unreachable code and obfuscate symbols, and that a class name arriving as a string from a network response is invisible to this analysis. They want evidence that you can write a surgical ProGuard rule rather than disabling optimizations.

A GOOD ANSWER COVERS: A great answer hits four things in order. First, it writes the exact keep rule, for example -keep class com.example.MyClass { <init>(...); }, and notes that the class name and constructor must both be preserved. Second, it explains why R8 breaks the feature, namely that Class.forName with a dynamic string is not a compile-time dependency edge, so R8 sees the target class as dead code and renames or removes it. Third, it distinguishes -keep, which preserves the class name and members, from -keepclassmembers, which preserves members but still allows the class itself to be renamed, which would break the string lookup. Fourth, it mentions that if many classes share a pattern, a wildcard rule like -keep class * extends com.example.BasePlugin { <init>(...); } is the scalable alternative.

COMMON WRONG ANSWERS: There are three common red flags. First, offering a vague or overly broad rule such as -keep class * or -keep class com.example.** { *; }, which shows you do not understand the cost of disabling tree-shaking. Second, suggesting -keepattributes without any -keep rule on the class, which preserves metadata but does not prevent the class from being removed or renamed. Third, stating that you would simply set minifyEnabled false or add android:keep to the manifest, demonstrating that you reach for a sledgehammer instead of a scalpel.

LIKELY FOLLOW-UPS: The interviewer may push on scale by asking how you would handle dozens of reflectively loaded plugin classes, where you should answer with an interface-based wildcard rule or the androidx.annotation.Keep annotation. They may ask how you would avoid reflection entirely, leading to discussion of ServiceLoader, Dagger multibindings, or generated factories. They might also ask how you validate the fix, which should involve running a release build, checking mapping.txt for the expected class name, and executing the dynamic instantiation path on a physical device.

ONE CONCRETE EXAMPLE: Imagine a server returns the string com.example.PaymentGateway and your code calls Class.forName(response).getDeclaredConstructor().newInstance(). After R8, the class is renamed to something like a.b.c and its constructor is inlined away. The precise fix is -keep class com.example.PaymentGateway { public <init>(); }. If the constructor requires arguments, you must list the exact types, such as public <init>(android.content.Context);, because R8 needs to know which overload survives. Using the varargs shorthand <init>(...) is acceptable when you want to keep every constructor.

Source: developer.android.com

Read the original → developer.android.com

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.