How to Dynamically Manage Android Resources and Shrink APK Size
This article explains the design and implementation of a dynamic resource management system for Android apps, covering the motivation for reducing APK size, the architecture of the dynamicresbase, dynamicrescore, and dynamicresplugin modules, resource loading and application processes, Gradle plugin tasks, code transformation, and practical tips for handling .so libraries.
Overview
Dynamic resource management system used by Huolala to manage offline .so, animation, and font files, aiming to reduce APK size.
Motivation
APK size growing due to many .so libraries (over 35, each for arm64‑v8a and armeabi‑v7a).
Low‑frequency resources can be downloaded at runtime instead of being packaged.
Key Features
Resource classification (fonts, frame animations, .so, custom files).
Unified loading API for all resource types.
Configurable packaging via a Gradle plugin.
Architecture
Three modules:
dynamicresbase : common utilities such as MD5, zip handling, and the DynamicPkgInfo entity.
dynamicrescore : provides loading and application of resources (fonts, frame animations, .so, custom).
dynamicresplugin : Gradle plugin that performs resource packaging.
dynamicrescore is organized into five layers: external interface, resource application, loading flow (state‑machine), interface isolation, and concrete implementation.
Resource Loading Flow
Check if the resource version matches the local database; if so, verify local files.
If versions differ, start download.
Verify downloaded file (MD5, length, name).
If the file is a zip, unzip it.
Verify each extracted file.
Resource Application
Fonts are applied by creating a Typeface and setting it on a TextView; frame animations are applied via AnimationDrawable; .so files are loaded through a custom loader that handles missing libraries gracefully.
.so Dynamic Loading
The system determines the device’s supported ABIs (Build.SUPPORTED_ABIS) and selects the matching .so package. After download and verification, the .so directory is injected at the head of the ClassLoader’s nativeLibraryPathElements array using reflection, or the Relinker library is used to handle dependency resolution on Android N+.
private static void install(ClassLoader classLoader, File soFolder) throws Throwable {
Field pathListField = findField(classLoader, "pathList");
Object dexPathList = pathListField.get(classLoader);
Field nativeLibraryDirectories = findField(dexPathList, "nativeLibraryDirectories");
List<File> libDirs = (List<File>) nativeLibraryDirectories.get(dexPathList);
libDirs.add(0, soFolder);
// Additional reflection steps for different Android versions omitted for brevity
}Gradle Plugin (dynamicresplugin)
The plugin defines three main tasks:
TransformTask : uses ASM to replace System.loadLibrary calls with a custom SoLoadUtil.loadLibrary method.
DeleteAndCopySoTask : removes .so files from the APK during the build and copies them to a configurable external directory.
ZipResTask : compresses fonts, frame animations, and .so files, generates a DynamicPkgInfo Java class via JavaPoet, and optionally uploads the artifacts.
Task execution order is configured in dynamic_config.gradle, which also controls scanning rules, ABI filters, ignore lists, and whether to run in release builds.
// Example of dynamic_config.gradle snippet
dynamic_config = [
is_replace_load_library: false,
is_delete_so: false,
is_zip_res: false,
is_release_type: isReleaseBuildType(),
create_java_pkg_name: 'com.test',
// ... other configuration entries ...
]Implementation Highlights
Key code pieces include the ASM SystemLoadMethodVisitor that swaps the target method, the reflection‑based install method that injects native library paths, and the use of the Relinker library to resolve .so dependencies on newer Android versions.
Optimization Results
Applying the dynamic resource management system reduced the Huolala user‑side APK from 54 MB to 46 MB, achieving an 8 MB size reduction.
References
https://www.jianshu.com/p/260137fdf7c5 https://mp.weixin.qq.com/s/X58fK02imnNkvUMFt23OAg
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.
