R8, Shrinking & App Size Quiz

BUILD & TOOLING › Gradle

Which statement about shrinkResources is correct?

Answer: It only takes effect when minifyEnabled (R8) is also enabled.

Resource shrinking relies on R8's code reference graph, so shrinkResources is a no-op unless minifyEnabled is true; reflective references must be declared manually (e.g., keep.xml).

Which library most commonly requires you to hand-write keep rules for your model classes because it doesn't ship adequate consumer rules?

Answer: Gson

Gson resolves fields purely by runtime reflection and ships no rules for your models, so R8 renames them and breaks parsing. Hilt, kotlinx.serialization, and Moshi codegen bundle their own consumer ProGuard rules.

What is the purpose of R8's mapping.txt?

Answer: Map obfuscated names back to originals to read release stack traces.

mapping.txt records the original-to-obfuscated name mapping for a build; you retrace or upload it to the Play Console to read crashes. Keep declarations live in proguard-rules.pro, not mapping.txt.

Compared to compatibility mode, what should you expect from R8 full mode (the modern default)?

Answer: Tougher optimization, stricter reflection assumptions, may need keep rules.

Full mode makes stronger assumptions (it assumes no unexpected reflection), enabling more aggressive shrinking and optimization, which can demand additional keep rules for reflective code.

An Android App Bundle lets Google Play generate optimized split APKs primarily along which dimensions?

Answer: Screen density, ABI/architecture, and language

Play splits the bundle by screen density, ABI, and language so each device downloads only the matching configuration, which is the core size benefit of the AAB format.

What distinguishes -keep from -keepclassmembers in an R8 rules file?

Answer: -keep keeps the class and its members; -keepclassmembers keeps only members, class removable.

-keep is the broad rule that retains the class plus members; -keepclassmembers is narrower, retaining only the specified members and allowing the class itself to be shrunk away if nothing references it.

Why should resources.arsc be left uncompressed inside the APK/bundle?

Answer: ART memory-maps directly into it, so compressing it hurts performance.

ART and the framework read resources.arsc by seeking/mmapping into the file directly; compressing it forces full decompression and degrades runtime performance, so it is kept stored, not deflated.

What does the androidx.annotation @Keep annotation do when R8 is enabled?

Answer: It tells R8 to keep the annotated class or member from shrink or rename.

@Keep is honoured by the default optimize rules, which keep @Keep-annotated classes and members from being tree-shaken or obfuscated, making it a code-local alternative to a -keep rule.

Which Play Feature Delivery mode downloads a dynamic feature module only when the running app explicitly requests it via SplitInstallManager?

Answer: on-demand delivery

On-demand delivery fetches the module at runtime when the app calls SplitInstallManager, keeping it out of the initial install. Install-time and conditional modes decide inclusion at install time, and instant delivery is not a feature-module mode.

When deobfuscating a production stack trace with retrace, why must you use the mapping.txt generated for that exact build?

Answer: R8 renames members differently each build, so a mismatched mapping is wrong.

Each R8 run renames members independently, so a mapping from a different build maps the obfuscated names to the wrong originals; only the matching build's mapping.txt yields a correct retrace.

Why might you replace a Kotlin enum with an @IntDef in size-sensitive code?

Answer: Each enum adds about 1.0–1.4 KB to the DEX; @IntDef stays as ints with checks.

An enum generates a full class plus value array that costs about a kilobyte or more in classes.dex; @IntDef provides the same type safety at compile time but reduces to ordinary int constants R8 keeps tiny.

What distinguishes proguard-android-optimize.txt from the plain proguard-android.txt default config?

Answer: It also enables R8 optimizations like inlining and class merging.

The plain default historically suppresses optimizations; proguard-android-optimize.txt removes that restriction so R8 can inline, merge classes, and rewrite dead branches in addition to shrinking and obfuscating.

In the modern Android build toolchain, which earlier tool did R8 replace, and what additional job does R8 fold into the same pipeline?

Answer: It replaced ProGuard for shrinking and obfuscation, and it also dexes.

R8 is the default replacement for ProGuard, and because it is built on top of D8 it shrinks, optimizes, obfuscates, and dexes in one pass rather than running a separate dexing step.

What is the effect of the modern default android.packaging.jniLibs.useLegacyPackaging = false for native libraries?

Answer: It stores the .so files uncompressed and page-aligned for direct loading.

With legacy packaging off, native libraries are stored uncompressed and page-aligned inside the APK, so they are loaded directly from the package instead of being decompressed and extracted to disk at install time.

Back to R8, Shrinking & App Size