App Bundles
An App Bundle is a publishing format, not an install format. You upload one file; Play generates a different set of APKs for every device — and that change is why APKs are no longer universal.
For most of Android's life, an APK was a universal artefact: one file contained every language, every density and every CPU architecture, and a device took what it needed. App Bundles invert that. You upload everything once, and the store builds a bespoke set of APKs for each device.
TL;DR#
- An AAB (Android App Bundle) is an upload format. Devices do not install it.
- Play (or
bundletool) generates a split set per device: a base APK plus configuration splits for ABI, density and language. - Dynamic feature modules can be delivered at install time, soon after, or on demand.
- Google Play has required AABs for new apps since 2021.
- The signature is over the bundle from the developer's perspective, but devices verify the generated APKs — which is why Play App Signing exists.
Mental model#
Think of a made-to-measure clothing shop.
You bring the fabric and the patterns (the bundle). The shop does not hand you a bolt of cloth. It takes your measurements (the device's ABI, density, language) and cuts a garment that fits you (the split set). Everyone gets a different garment from the same pattern book.
Where the analogy breaks#
A tailor keeps the leftover cloth. Play does not ship the unused splits at all, but it does keep them available: if the user changes their language or plugs in a device with a different ABI, a different split may be needed later. The set is not immutable for the lifetime of the install.
The big picture#
Text description of this diagram
A five-stage pipeline. The app bundle contains base code and resources plus each dynamic feature module, keeping every density, ABI and language. The bundle manifest declares the modules and their delivery conditions. Play or bundletool selects the matching ABI, density and language resources and emits split APKs for one device. Delivery is install-time, fast-follow or on-demand. The package manager records the splits as one package sharing one UID, one data directory and one signature.
The key idea: nothing device-irrelevant is downloaded, but everything is still available if the device's configuration changes.
Deep dive#
What is in a bundle#
An AAB is a ZIP with a different contract from an APK:
BundleConfig.pb— bundle metadata and the compression/targeting configuration.base/— the base module: manifest, dex, resources.- One directory per dynamic feature module.
BUNDLE-METADATA/— signing and other metadata.
The base module is mandatory. Dynamic features are optional units that can be delivered later.
Split types#
Play generates several kinds of split:
| Split | Contents | Why |
|---|---|---|
| Base | core code and resources | always installed |
| ABI | lib/<abi>/ for one ABI | avoids shipping four architectures |
| Density | drawables for one density | avoids shipping every density |
| Language | values-<lang>/ | avoids shipping every translation |
| Dynamic feature | an optional module's code and resources | on-demand delivery |
The device installs them as one package: one UID, one data directory, one signature, one entry in the launcher.
Delivery modes for dynamic features#
A dynamic feature module declares a delivery condition:
- Install-time — included with the base install.
- Fast-follow — downloaded automatically soon after install, without blocking first launch.
- On-demand — downloaded when the app asks for it.
On-demand is the one that changes app architecture. The app must handle the module not being present: check whether it is installed, request it, show progress, and handle failure. The Play Core library provides the API; the app provides the UX.
Why Play required bundles#
From 2021, Google Play has required new apps to be published as App Bundles rather than APKs. The stated reason is size: a device downloads only what it needs, and the savings compound across languages, densities and ABIs.
The architectural consequence is larger than the size saving: the store now generates the installable artefact, which means the store is in the build path. That is why Play App Signing became the default — the developer uploads with an upload key, and Google re-signs the generated APKs with the app signing key.1
bundletool, and testing without Play#
bundletool is the open-source tool that does what Play does: it takes an AAB and generates APKs. It
can:
- build a universal APK (all splits combined) for testing,
- generate a device-specific set from a device spec,
- install a set to a connected device.
That is how you test an AAB locally, and how CI verifies that a bundle produces a valid split set.
What bundles do not solve#
Bundles reduce download size, not app size on disk in every case. A few caveats:
- All splits still count toward the install. The app's on-disk footprint is the sum of installed splits, and a user who changes language may accumulate more.
- Dynamic features add complexity. The delivery API, the loading states and the fallbacks are real engineering work.
- Not all stores use bundles. AAB is a Play format; other stores may still want a universal APK.
The last point matters for distribution strategy: an app that relies on on-demand modules is relying on Play's delivery machinery, not just on an Android platform feature.
Bundles and R8#
Bundles change the calculus for code shrinking. Because every split is generated per device, shipping unused code is less costly for a given user — but the total uploaded bundle still contains everything. R8 remains the right answer for reducing the base module, and it interacts with dynamic features: each feature module is shrunk independently, so a class used only by an on-demand feature should not be kept in the base.2
How it evolved#
| API | Version | What changed |
|---|---|---|
| 3 | 1.5 Cupcake |
|
| 12 | 3.1 Honeycomb |
|
| 15 | 4.0.3 Ice Cream Sandwich |
|
| 19 | 4.4 KitKat |
|
| 24 | 7.0 Nougat |
|
| 29 | 10 |
|
| 30 | 11 |
|
| 33 | 13 Tiramisu |
|
| 34 | 14 Upside Down Cake |
|
| 37 | 17 Cinnamon Bun |
|
Under the hood#
- PackageInstaller.javathe session API that installs split sets
- SplitAssetLoader.javahow splits are loaded as one package
- ApplicationInfo.javasplit names and the base/split relationship
- the build-side packaging tools
Curiosity corner#
- An AAB is a ZIP, but it is not installable.
adb install app.aabfails;bundletoolis how you turn it into something a device accepts. - The universal APK still exists as a test artefact. It is what
bundletoolproduces when you ask for a build that runs on any device. - A split can be added after install. If the user adds a language, the language split can arrive later — the install is not frozen at first download.
Try it#
bundletool build-apks --bundle=app.aab --output=app.apks
bundletool build-apks --bundle=app.aab --output=app.apks --connected-device
bundletool install-apks --apks=app.apks
unzip -l app.apks | head -20
Example output
Base APK: base-master.apk
Split: base-arm64_v8a.apk
Split: base-xxhdpi.apk
Split: base-fr.apk
Split: feature-camera.apkThe --connected-device flag is the interesting one: it asks the attached device for its spec and
generates only the splits that device would receive.
Check yourself#
Check yourself
An APK is fundamentally which kind of file?
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsAbout Android App Bundlesdeveloper.android.comaccessed 2026-10-27↩
- docsShrink, obfuscate, and optimize your appdeveloper.android.comaccessed 2026-10-27↩
- docsApp manifest overviewdeveloper.android.comaccessed 2026-10-27↩
- docsAPK signature schemesource.android.comaccessed 2026-10-27↩
- docsapksignerdeveloper.android.comaccessed 2026-10-27↩