Mobile Development 12 min read

R File Slimming and ID Inlining for Android Pluginized Applications

This article examines the problem of growing APK sizes caused by R.java files in JD.com's Android pluginized client, analyzes the feasibility of R file slimming, and presents a Gradle Transform and ASM‑based solution that inlines resource IDs, removes R files, and achieves 3‑5% size reduction without noticeable build‑time impact.

JD Retail Technology
JD Retail Technology
JD Retail Technology
R File Slimming and ID Inlining for Android Pluginized Applications

As the JD.com Android client evolves, added business logic and resources increase the APK size; analysis shows that R class files alone account for about 3%–5% of the package in both host and plugin modules.

R.java files are generated by the Android Asset Packaging Tool (aapt) to provide unique IDs for externalized resources, allowing developers to reference resources via R.layout.xxx or R.id.xxx .

Investigation of the feasibility of slimming R files revealed that, after decompiling plugin APKs, the R IDs are not inlined and the files occupy a measurable portion of the bundle, prompting the design of a custom Gradle plugin for ID inlining and R file removal.

The solution uses the Android Gradle Transform API to collect all compiled .class files, then applies ASM to visit R classes, extract static final int fields, and replace every GETSTATIC reference with the concrete integer value. Example Transform code: @Override public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { super.transform(transformInvocation); // collect inputs List allDirs = new ArrayList<>(transformInvocation.getInputs().size()); List allJars = new ArrayList<>(transformInvocation.getInputs().size()); for (TransformInput input : transformInvocation.getInputs()) { for (DirectoryInput dir : input.getDirectoryInputs()) { allDirs.add(dir.getFile()); } for (JarInput jar : input.getJarInputs()) { allJars.add(jar.getFile()); } } }

For non‑R classes, ASM’s MethodVisitor intercepts field instructions, looks up the stored ID value, and injects it with mv.visitLdcInsn(value) . Sample visitor snippet: @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { if (opcode == Opcodes.GETSTATIC) { Object value = jdRstore.getRFieldValue(owner, name); if (value != null) { mv.visitLdcInsn(value); return; } // fallback to public resources value = getPublicRFileValue(name); if (value != null) { mv.visitLdcInsn(value); return; } } super.visitFieldInsn(opcode, owner, name, desc); }

Public resources are handled by enabling aapt2 stable IDs ( --stable-ids and --emit-ids ), generating a shared_res_public.xml that maps resource names to fixed IDs. The plugin reads this file, stores the mappings, and applies the same inlining logic to public R IDs.

After integrating the R‑file slimming plugin, both host and plugin modules showed a consistent 3%–5% reduction in APK size, and crash testing confirmed that resource‑not‑found errors were eliminated by also inlining public IDs. Measurements indicated no significant increase in overall build time.

In summary, the custom Gradle Transform + ASM approach effectively reduces APK size by removing unnecessary R class files and inlining resource IDs, while maintaining stability and performance, and serves as a reference for similar mobile development optimization efforts.

Androidplugin-architectureGradle PluginAPK sizer-fileResource ID
JD Retail Technology
Written by

JD Retail Technology

Official platform of JD Retail Technology, delivering insightful R&D news and a deep look into the lives and work of technologists.

0 followers
Reader feedback

How this landed with the community

login 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.