Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Uber] Add full-stack Android App Bundle support for dynamic feature modules#520

Open
oliviernotteghem wants to merge 1 commit into
bazelbuild:main from
oliviernotteghem:origin_dynamic_features
Open

[Uber] Add full-stack Android App Bundle support for dynamic feature modules #520
oliviernotteghem wants to merge 1 commit into
bazelbuild:main from
oliviernotteghem:origin_dynamic_features

Conversation

@oliviernotteghem

@oliviernotteghem oliviernotteghem commented Jun 5, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

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:

  • //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.

...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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /