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's really being asked
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.
The full answer
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.
The mistakes people make
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.
What usually comes next
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.
A 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.
Interview question
Your app calls Class.forName(response).getDeclaredConstructor(Context.class).newInstance(). Which keep rule is both correct and most precise?
- a.-keep class com.example.PaymentGateway { public <init>(); }
- b.-keep class com.example.PaymentGateway { public <init>(android.content.Context); }Correct
- c.-keep class com.example.** { *; }
- d.-keepclassmembers class com.example.PaymentGateway { public <init>(...); }
Why? this is the answer
The correct answer must use -keep (not -keepclassmembers) to prevent R8 from renaming the class so Class.forName can find it, and it must specify the exact Context constructor because R8 needs to know which overload survives. Option D is tempting because it preserves constructors, but it still allows the class itself to be renamed or removed, which breaks dynamic string lookup.
Just read this? Test yourself on what you have been reading.
Read the original → developer.android.com
- #android
- #r8
- #proguard
- #reflection
- #build-optimization
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on android — each one lists the topics its interview covers.
See open roles