Mobile Development 13 min read

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.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
How AGP 4.1.0 Eliminates Redundant R Files and Boosts Build Speed

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 build output
AGP 3.5.0 build output

AGP 3.5.0 output:

AGP 3.6.0 build output
AGP 3.6.0 build 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 3.6.0 APK inspection
AGP 3.6.0 APK inspection

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.

AGP 4.1.0 APK inspection
AGP 4.1.0 APK inspection

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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AndroidR8Build OptimizationAGPGradleAPK sizeR class
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.