0

Merge android_app_bundle.md into android_dynamic_feature_modules.md

These make more sense as one.

Change-Id: I3fa6e52fbbf3d2cd83f87dea066d814371761e7b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3097706
Commit-Queue: Andrew Grieve <agrieve@chromium.org>
Auto-Submit: Andrew Grieve <agrieve@chromium.org>
Reviewed-by: Samuel Huang <huangs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#912697}
This commit is contained in:
Andrew Grieve
2021-08-17 19:43:59 +00:00
committed by Chromium LUCI CQ
parent d3ad25ace1
commit 7e777abf7e
6 changed files with 57 additions and 223 deletions

@ -1,7 +1,7 @@
# Android Build Docs
* [//docs/android_build_instructions.md](/docs/android_build_instructions.md)
* [android_app_bundles.md](android_app_bundles.md)
* [//docs/android_dynamic_feature_modules.md](/docs/android_dynamic_feature_modules.md)
* [build_config.md](build_config.md)
* [coverage.md](coverage.md)
* [java_toolchain.md](java_toolchain.md)

@ -1,205 +0,0 @@
# Introduction
This document describes how the Chromium build system supports Android app
bundles.
[TOC]
# Overview of app bundles
An Android app bundle is an alternative application distribution format for
Android applications on the Google Play Store, that allows reducing the size
of binaries sent for installation to individual devices that run on Android L
and beyond. For more information about them, see the official Android
[documentation](https://developer.android.com/guide/app-bundle/).
For the context of this document, the most important points are:
- Unlike a regular APK (e.g. `foo.apk`), the bundle (e.g. `foo.aab`) cannot
be installed directly on a device.
- Instead, it must be processed into a set of installable split APKs, which
are stored inside a special zip archive (e.g. `foo.apks`).
- The splitting can be based on various criteria: e.g. language or screen
density for resources, or cpu ABI for native code.
- The bundle also uses the notion of dynamic features modules (DFMs) to
separate several application features. Each module has its own code, assets
and resources, and can be installed separately from the rest of the
application if needed.
- The main application itself is stored in the '`base`' module (this name
cannot be changed).
# Declaring app bundles with GN templates
Here's an example that shows how to declare a simple bundle that contains a
single base module, which enables language-based splits:
```gn
# First declare the first bundle module. The base module is the one
# that contains the main application's code, resources and assets.
android_app_bundle_module("foo_base_module") {
# Declaration are similar to android_apk here.
...
}
# Second, declare the bundle itself.
android_app_bundle("foo_bundle") {
# Indicate the base module to use for this bundle
base_module_target = ":foo_base_module"
# The name of our bundle file (without any suffix). Default would
# be 'foo_bundle' otherwise.
bundle_name = "FooBundle"
# Enable language-based splits for this bundle. Which means that
# resources and assets specific to a given language will be placed
# into their own split APK in the final .apks archive.
enable_language_splits = true
# Proguard settings must be passed at the bundle, not module, target.
proguard_enabled = !is_java_debug
}
```
When generating the `foo_bundle` target with Ninja, you will end up with
the following:
- The bundle file under `out/Release/apks/FooBundle.aab`
- A helper script called `out/Release/bin/foo_bundle`, which can be used
to install / launch / uninstall the bundle on local devices.
This works like an APK wrapper script (e.g. `foo_apk`). Use `--help`
to see all possible commands supported by the script.
# Declaring dynamic feature modules with GN templates
Please see
[Dynamic Feature Modules](../../../docs/android_dynamic_feature_modules.md) for
more details. In short, if you need more modules besides the base one, you
will need to list all the extra ones using the extra_modules variable which
takes a list of GN scopes, as in:
```gn
android_app_bundle_module("foo_base_module") {
...
}
android_app_bundle_module("foo_extra_module") {
...
}
android_app_bundle("foo_bundle") {
base_module_target = ":foo_base_module"
extra_modules = [
{ # NOTE: Scopes require one field per line, and no comma separators.
name = "my_module"
module_target = ":foo_extra_module"
}
]
...
}
```
Note that each extra module is identified by a unique name, which cannot
be '`base`'.
# Bundle signature issues
Signing an app bundle is not necessary, unless you want to upload it to the
Play Store. Since this process is very slow (it uses `jarsigner` instead of
the much faster `apkbuilder`), you can control it with the `sign_bundle`
variable, as described in the example above.
The `.apks` archive however always contains signed split APKs. The keystore
path/password/alias being used are the default ones, unless you use custom
values when declaring the bundle itself, as in:
```gn
android_app_bundle("foo_bundle") {
...
keystore_path = "//path/to/keystore"
keystore_password = "K3y$t0Re-Pa$$w0rd"
keystore_name = "my-signing-key-name"
}
```
These values are not stored in the bundle itself, but in the wrapper script,
which will use them to generate the `.apks` archive for you. This allows you
to properly install updates on top of existing applications on any device.
# Proguard and bundles
When using an app bundle that is made of several modules, it is crucial to
ensure that proguard, if enabled:
- Keeps the obfuscated class names used by each module consistent.
- Does not remove classes that are not used in one module, but referenced
by others.
To achieve this, a special scheme called *synchronized proguarding* is
performed, which consists of the following steps:
- The list of unoptimized .jar files from all modules are sent to a single
proguard command. This generates a new temporary optimized *group* .jar file.
- Each module extracts the optimized class files from the optimized *group*
.jar file, to generate its own, module-specific, optimized .jar.
- Each module-specific optimized .jar is then sent to dex generation.
This synchronized proguarding step is added by the `android_app_bundle()` GN
template. In practice this means the following:
- `proguard_enabled` must be passed to `android_app_bundle` targets, but not
to `android_app_bundle_module` ones.
- `proguard_configs` can be still passed to individual modules, just
like regular APKs. All proguard configs will be merged during the
synchronized proguard step.
# Manual generation and installation of .apks archives
Note that the `foo_bundle` script knows how to generate the .apks archive
from the bundle file, and install it to local devices for you. For example,
to install and launch a bundle, use:
```sh
out/Release/bin/foo_bundle run
```
If you want to manually look or use the `.apks` archive, use the following
command to generate it:
```sh
out/Release/bin/foo_bundle build-bundle-apks \
--output-apks=/tmp/BundleFoo.apks
```
All split APKs within the archive will be properly signed. And you will be
able to look at its content (with `unzip -l`), or install it manually with:
```sh
build/android/gyp/bundletool.py install-apks \
--apks=/tmp/BundleFoo.apks \
--adb=$(which adb)
```
The task of examining the manifest is simplified by running the following,
which dumps the application manifest as XML to stdout:
```sh
build/android/gyp/bundletool.py dump-manifest
```

@ -2884,8 +2884,7 @@ if (enable_java_templates) {
}
} else {
# Dex generation for app bundle modules with proguarding enabled takes
# place later due to synchronized proguarding. For more details,
# read build/android/docs/android_app_bundles.md
# place later due to synchronized proguarding.
_final_dex_target_name = "${_template_name}__final_dex"
dex(_final_dex_target_name) {
forward_variables_from(invoker,

@ -1,7 +1,6 @@
# Chrome on Android App Bundles and Dynamic Feature Modules
This directory contains GN templates and code for Chrome-specific
[Android app bundles](/build/android/docs/android_app_bundles.md) and
[dynamic feature modules](/docs/android_dynamic_feature_modules.md).
Among others, it offers the following:

@ -282,12 +282,9 @@ used when committed.
Java related issues at compile time with the 'lint' tool.
* [Java Code Coverage](../build/android/docs/coverage.md) - Collecting code
coverage data with the EMMA tool.
* [Android BuildConfig files](../build/android/docs/build_config.md) -
What are .build_config files and how they are used.
* [Android App Bundles](../build/android/docs/android_app_bundles.md) -
How to build Android app bundles for Chrome.
* [Dynamic Feature Modules (DFMs)](android_dynamic_feature_modules.md) - How
to create dynamic feature modules.
* [Dynamic Feature Modules (DFMs)](android_dynamic_feature_modules.md) - What
are they and how to create new ones.
* [Other build-related Android docs](../build/android/docs/index.md)
* [Chrome for Android UI](ui/android/overview.md) - Resources and best practices for
developing UI

@ -12,6 +12,8 @@ Bundles provide three main advantages over monolithic `.apk` files:
1. Language resources are split into language-specific `.apk` files, known as
"resource splits". Delivering only the active languages reduces the overhead
of UI strings.
* Resource splits can also be made on a per-screen-density basis (for drawables),
but Chrome has not taken advantage of this (yet).
2. Features can be packaged into lazily loaded `.apk` files, known as
"feature splits". Feature splits have no performance overhead until used.
* Except on versions prior to Android O, where support for
@ -38,24 +40,57 @@ Adding new features vis feature splits is highly encouraged when it makes sense
to do so:
* Has a non-trivial amount of Dex (>50kb)
* Not needed on startup
* Has a small integration surface (calls into it must be done with reflection).
* Has a small integration surface (calls into it must be done with reflection)
* Not used by WebView (WebView does not support DFMs)
***
The remainder of this doc focuses on DFMs.
[android_build_instructions.md#multiple-chrome-targets]: android_build_instructions.md#multiple-chrome-targets
[Android App Bundles]: https://developer.android.com/guide/app-bundle
[android:isolatedSplits]: https://developer.android.com/reference/android/R.attr#isolatedSplits
[go/isolated-splits-dev-guide]: http://go/isolated-splits-dev-guide
## Limitations
### Declaring App Bundles with GN Templates
DFMs have the following limitations:
Here's an example that shows how to declare a simple bundle that contains a
single base module, which enables language-based splits:
* **WebView:** We don't support DFMs for WebView. If your feature is used by
WebView you cannot put it into a DFM.
```gn
android_app_bundle_module("foo_base_module") {
# Declaration are similar to android_apk here.
...
}
## Getting started
android_app_bundle("foo_bundle") {
base_module_target = ":foo_base_module"
# The name of our bundle file (without any suffix).
bundle_name = "FooBundle"
# Enable language-based splits for this bundle. Which means that
# resources and assets specific to a given language will be placed
# into their own split APK in the final .apks archive.
enable_language_splits = true
# Proguard settings must be passed at the bundle, not module, target.
proguard_enabled = !is_java_debug
}
```
When generating the `foo_bundle` target with Ninja, you will end up with
the following:
* The bundle file under `out/Release/apks/FooBundle.aab`
* A helper script called `out/Release/bin/foo_bundle`, which can be used
to install / launch / uninstall the bundle on local devices.
This works like an APK wrapper script (e.g. `foo_apk`). Use `--help`
to see all possible commands supported by the script.
The remainder of this doc focuses on DFMs.
## Declaring Dynamic Feature Modules (DFMs)
This guide walks you through the steps to create a DFM called _Foo_ and add it
to the Chrome bundles.
@ -207,6 +242,15 @@ $ adb shell dumpsys package org.chromium.chrome | grep splits
> splits=[base, config.en]
```
*** note
The wrapper script's `install` command does approximately:
```sh
java -jar third_party/android_build_tools/bundletool/bundletool-all-$VERSION.jar build-apks --output tmp.apks ...
java -jar third_party/android_build_tools/bundletool/bundletool-all-$VERSION.jar install-apks --apks tmp.apks
```
The `install-apks` command uses `adb install-multiple` under-the-hood.
***
### Adding Java code