How AGP 4.1.0 Eliminates Redundant R Files and Boosts Build Speed
Upgrading from AGP 3.5.0 to 4.1.0 dramatically simplifies R class generation, removes redundant R files via R8‑based inlining, reduces APK size by ~7 MB, and speeds up release builds by over ten minutes, as demonstrated through detailed build‑artifact comparisons and Gradle task analysis.
Background
The static code analysis upgrade required a newer Android Gradle Plugin (AGP). The Cloud Music project originally used AGP 3.5.0, so the team compared it with AGP 3.6.0 and AGP 4.1.0.
AGP 3.6.0 – Simplified R class generation
AGP 3.6.0 generates a single R.class for each library module and shares it with dependent modules, removing the intermediate R.java → javac → R.class step. Each module must have a unique package name, and the visibility of the generated R class depends on the dependency configuration ( api vs implementation).
Each module directly produces R.class instead of the previous two‑step process.
Build artifact comparison:
AGP 3.5.0 output:
The reduction of the intermediate R generation step improves build efficiency.
AGP 4.1.0 – R class shrinking and inlining
Starting with AGP 4.1.0, fields of R classes are no longer kept by default. When code shrinking (e.g., R8) is enabled, references to R fields are replaced with their constant values, allowing the R class to be removed and reducing APK size dramatically—provided the app does not access R classes via reflection.
R file redundancy problem
Each library module historically generated its own static R class to avoid ID conflicts. During APK packaging the final R file is the accumulation of all library R files plus the app’s own R, leading to redundant entries that bloat the APK, especially in deep dependency trees.
R_lib1 = R_lib1;
R_lib2 = R_lib2;
R_lib3 = R_lib3;
R_biz1 = R_lib1 + R_lib2 + R_lib3 + R_biz1;
R_biz2 = R_lib2 + R_lib3 + R_biz2;
R_app = R_lib1 + R_lib2 + R_lib3 + R_biz1 + R_biz2 + R_app;R file inlining solution
A custom transform rewrites all library‑module R references to point to the top‑level R_app and then deletes the library R files, leaving only a single top‑level R in the APK. A more aggressive variant replaces every R reference with a constant and removes the top‑level R entirely.
Experimental comparison
Two builds were created: one with AGP 3.6.0 and another with AGP 4.1.0. Both enabled ProGuard shrinking ( minifyEnabled true) but disabled obfuscation ( -dontobfuscate) to keep class names readable.
buildTypes {
release {
minifyEnabled true // enable shrinking
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}AGP 3.6.0 APK inspection shows the bizlib module still contains an R class, and SecondActivity bytecode references it.
AGP 4.1.0 APK inspection shows the bizlib module no longer contains an R class, and the bytecode of SecondActivity contains a constant instead of an R reference.
How R inlining is implemented
The inlining occurs in the minifyReleaseWithR8 task. A Gradle script was added to print task inputs and outputs, revealing that the whole app’s R JAR is an input to this task.
gradle.taskGraph.afterTask { task ->
try {
println("---- task name:" + task.name)
println("-------- inputs:")
task.inputs.files.each { println(it.absolutePath) }
println("-------- outputs:")
task.outputs.files.each { println(it.absolutePath) }
} catch (Exception e) {}
}The task is an instance of R8Task:
class CreationAction(...):
override val type = R8Task::class.java
// create minifyReleaseWithR8 task
override val name = computeTaskName("minify", "WithR8")
...During execution the task invokes the R8 tool:
fun runR8(
inputClasses: Collection<Path>,
...
) {
ClassFileProviderFactory(libraries).use { libraryClasses ->
ClassFileProviderFactory(classpath).use { classpathClasses ->
r8CommandBuilder.addLibraryResourceProvider(libraryClasses.orderedProvider)
r8CommandBuilder.addClasspathResourceProvider(classpathClasses.orderedProvider)
R8.run(r8CommandBuilder.build())
}
}
}R8 performs shrinking, optimization, and dex conversion. While shrinking it replaces references to R fields with their constant values, after which the R class can be removed. The default keep rule for R fields (
-keepclassmembers class **.R$* { public static <fields>; }) was removed in AGP 4.1.0, enabling this behavior.
R8 combines ProGuard and D8 capabilities, handling class‑to‑dex conversion, shrinking, and constant propagation.
Results
AGP 4.1.0 eliminates redundant R files by inlining them through R8.
In the test project the APK size decreased by approximately 7 MB.
Release build time was reduced by more than ten minutes due to task merging and the simplified R generation pipeline.
Test project used for the analysis: https://github.com/sunday1937/AgpTest2
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
