[Uber] Add full-stack Android App Bundle support for dynamic feature modules#520
Open
oliviernotteghem wants to merge 1 commit into
Open
[Uber] Add full-stack Android App Bundle support for dynamic feature modules#520oliviernotteghem wants to merge 1 commit into
oliviernotteghem wants to merge 1 commit into
Conversation
…modules
Implements end-to-end support for Android App Bundles (AAB) with dynamic
feature modules in rules_android, covering all four artifact types:
resources, bytecode/dex, native libraries, and assets.
Resources
Resource tables from all dynamic features are aggregated into the base
app's AAPT2 resource compilation step so that the final bundle contains
a single, unified R.txt. Each feature's proto-apk_ is built separately
(via a dedicated android_feature_module_rule target) and merged into the
base bundle at packaging time. Manifest entries from feature modules are
filtered to prevent leaking into the base manifest, and manifest merging
order is corrected to match Play's expectations.
Bytecode / DEX
Each feature module's deploy jar is passed through R8 alongside the base
deploy jar. Before invocation, a filtering step (filter_feature_classes.sh)
identifies all classes already present in the base dex output and strips
them from each feature jar — preventing duplicate class definitions and
keeping per-feature dex splits as small as possible. Proguard specs are
collected transitively across all feature dependencies and supplied to R8
via --pg-conf, with --pg-compat enabled for compatibility. The resulting
per-feature dex directories are zipped and embedded in the final AAB.
Native libraries
Native .so files are extracted from each feature's unsigned APK (lib/*),
filtered to remove non-library entries, and merged into the feature's
bundle zip alongside its resources and dex. Only the ABI slice matching
the target device is delivered by Play at install time.
Asset packs / is_asset_pack
Feature modules declared as asset packs (is_asset_pack = True) are handled
as a separate code path that skips dex compilation entirely and packages
only the asset tree and manifest.
Resource shrinking and obfuscation
Two new flags on android_application — shrink_resources and
obfuscate_resources — wire R8's resource shrinker and AAPT2's optimize
step into the pipeline. On Uber Rider this reduces binary size by ~1.5 MB
(unused resource pruning) and ~0.5 MB (path minification) respectively.
Startup profile (PGO)
An optional startup_profile argument threads a Baseline Profile through
R8's --startup-profile flag for profile-guided optimization.
This implementation has been rolled out to production across all Uber
Android apps on the Play Store. Apps like Uber Rider ship with nearly a
dozen dynamic feature modules — each carrying its own assets, resources,
bytecode, and native libraries — and are built end-to-end using this
pipeline today.
Test Plan
From examples/bundle/, run:
# All four feature module types
bazel build //features/assets:feature_module # asset-only feature, no dex
bazel build //features/resources:feature_module # resource-only feature, no dex
bazel build //features/dex:feature_module # feature with bytecode (guava dep)
bazel build //features/native:feature_module # feature with native .so (ffmpeg-kit dep)
# Full android_application bundles integrating feature modules
bazel build //app:assets # base app + asset feature module
bazel build //app:native # base app + native library feature module
Each target exercises a different slice of the pipeline:
- //features/assets — asset pack code path (is_asset_pack, no dex compilation)
- //features/resources — resource-only feature, AAPT2 proto-apk_ generation
- //features/dex — Java bytecode compilation + R8 filtering in feature mode
- //features/native — native .so extraction and jni/ layout conversion in AAR
- //app:assets — base app + feature manifest merging + bundle assembly
- //app:native — end-to-end: base app + native libs + manifest merging + bundle
Attributions
Developed at Uber (Olivier Notteghem @oliviernotteghem ) in
collaboration with Snap Inc (Mauricio Gonzalez - @mauriciogg ) from Snap's Android build
infrastructure team.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements end-to-end support for Android App Bundles (AAB) with dynamic
feature modules in rules_android, covering all four artifact types:
resources, bytecode/dex, native libraries, and assets.
Resources
Resource tables from all dynamic features are aggregated into the base
app's AAPT2 resource compilation step so that the final bundle contains
a single, unified R.txt. Each feature's proto-apk_ is built separately
(via a dedicated android_feature_module_rule target) and merged into the
base bundle at packaging time. Manifest entries from feature modules are
filtered to prevent leaking into the base manifest, and manifest merging
order is corrected to match Play's expectations.
Bytecode / DEX
Each feature module's deploy jar is passed through R8 alongside the base
deploy jar. Before invocation, a filtering step (filter_feature_classes.sh)
identifies all classes already present in the base dex output and strips
them from each feature jar — preventing duplicate class definitions and
keeping per-feature dex splits as small as possible. Proguard specs are
collected transitively across all feature dependencies and supplied to R8
via --pg-conf, with --pg-compat enabled for compatibility. The resulting
per-feature dex directories are zipped and embedded in the final AAB.
Native libraries
Native .so files are extracted from each feature's unsigned APK (lib/*),
filtered to remove non-library entries, and merged into the feature's
bundle zip alongside its resources and dex. Only the ABI slice matching
the target device is delivered by Play at install time.
Asset packs / is_asset_pack
Feature modules declared as asset packs (is_asset_pack = True) are handled
as a separate code path that skips dex compilation entirely and packages
only the asset tree and manifest.
Resource shrinking and obfuscation
Two new flags on android_application — shrink_resources and
obfuscate_resources — wire R8's resource shrinker and AAPT2's optimize
step into the pipeline. On Uber Rider this reduces binary size by ~1.5 MB
(unused resource pruning) and ~0.5 MB (path minification) respectively.
Startup profile (PGO)
An optional startup_profile argument threads a Baseline Profile through
R8's --startup-profile flag for profile-guided optimization.
This implementation has been rolled out to production across all Uber
Android apps on the Play Store. Apps like Uber Rider ship with nearly a
dozen dynamic feature modules — each carrying its own assets, resources,
bytecode, and native libraries — and are built end-to-end using this
pipeline today.
Test Plan
From examples/bundle/, run:
bazel build //features/assets:feature_module # asset-only feature, no dex
bazel build //features/resources:feature_module # resource-only feature, no dex
bazel build //features/dex:feature_module # feature with bytecode (guava dep)
bazel build //features/native:feature_module # feature with native .so (ffmpeg-kit dep)
Full android_application bundles integrating feature modules
bazel build //app:assets # base app + asset feature module
bazel build //app:native # base app + native library feature module
Each target exercises a different slice of the pipeline:
*Attributions
Developed at Uber (Olivier Notteghem @oliviernotteghem ) in
collaboration with Snap Inc (Mauricio Gonzalez - @mauriciogg ) from Snap's Android build
infrastructure team.