Android App Bundle Part-2 : BundleTool
I tried to explain Android App Bundle in my previous post. Before you read this part 2, I strongly recommend start with part 1 and get the better understanding for Android App bundle. This part will cover how to generate app bundle and generate apks with thebundletool
for the testing purpose.
Android App Bundle Part -1
Build App Bundle
Let’s play around with bundleTool and see the all possible ways to generate app bundle or apks.
- Clone/Download this sample open source application https://github.com/naman14/Timber, you can pick your application or any open source application. Thanks, Naman Dwivedi for this awesome application (github).
- Now, update the Android Gradle Plugin version to
3.2.0-alpha14 or above
in projectbuild.gradle
file to use the Android App Bundle feature, build/sync the project. - You might need to use the new gradle version too if that’s the case then change the gradle version in
gradle-wrapper.properties
file, build/sync the project.
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
4. Now time to generate the bundle and analyze it.
As I mentioned in my previous post there two ways to generate the bundle
- Using Android Studio, Go to Build > Build Bundle(s) / APK(s) and select Build Bundle(s), you will find the built bundle at :
./app/outputs/bundle/debug/bundle.aab
- Using command line,
./gradlew bundleDebug
from the root directory of project.
Test/Analyze App Bundle
These are the just ways to generate your application publishing bundle, Once you build your Android App Bundle, you should test/understand how Google Play uses it to generate APKs for the device. There are two ways you should consider testing your app bundle:
- Locally using the
bundletool
command line tool, download this tool from github, It’s open sourced by Google. - Through Google Play by uploading your bundle to the Play Console and using the new internal test track
Only the first option is a feasible option here. Let’s analyze this bundle in detailed and learn more about how it helps us to reduce the app size. We’ll use bundletool
command to analyze or generate apks as per our need. I discussed bundletool
in my previous post and how it will be helpful.
Note:
In this article, I’ve mentioned only bundletool
, instead of using the actual command : jar ~/Downloads/bundletool-all-0.3.3.jar
Actually, I’m using an alias for it which I’ve configured under ~/.bash_aliases
file.
You can also setup alias for java -jar ~/Downloads/bundletool-all-0.3.3.jar
part.
- Create ~/.bash_aliases file.
- Write the following line :
alias bundletool=’java -jar /Users/srpatel/bundletool-all-0.3.3.jar’
- run the command to load it : "source ~/.bash_aliases"
If you don’t want to use the aliases then use the actual command.
java -jar ~/Downloads/bundletool-all-0.3.3.jar build-apks
--bundle=your_bundle_path
--output=out_bundle_archive_set.apks
Generate all possible APKs archive set
- Generate an APK set archive using
bundletool
basic command, you will havetimber_app.apks
zip file.
bundletool build-apks
--bundle=app/build/outputs/bundle/debug/bundle.aab
--output=my_app.apks
Note : Make sure to add language apks configuration manually as there is bug with 3.2.0-alpha14 and 3.2.0-alpha15
versions, none of them generates language configuration apks. Open app/build.gradle and add the following inside the android {}
block:
bundle {
language {
enableSplit = true
}
}
- Unzip this apks zip archive file into the different directory. Wow !! That’s a lot of apks !! 🤔 😲 😲 You can see the every apk size and get the idea that how it helps to reduce the application size when it gets installed on the device.
If I just consider the en
language then there is a reduction of 1 MB in the application size, that’s huge !! 😄 😲, even I’ve not considered density
and abi
$ mkdir all_apks // create new dir.
$ unzip timber_app.apks -d all_apks
$ ls -lh all_apks | awk '{print $9, $5}'base-af.apk 10K
base-am.apk 12K
base-ar.apk 11K
base-arm64_v8a.apk 434K
base-armeabi_v7a.apk 415K
base-az.apk 11K
base-be.apk 12K
base-bg.apk 12K
base-bn.apk 13K
base-bs.apk 11K
base-ca.apk 11K
base-cs.apk 11K
base-da.apk 16K
base-de.apk 16K
base-el.apk 12K
base-en.apk 29K
base-es.apk 21K
base-et.apk 11K
base-eu.apk 11K
base-fa.apk 12K
base-fi.apk 10K
base-fr.apk 21K
base-gl.apk 11K
base-gu.apk 13K
base-hdpi.apk 2.4M
base-hi.apk 13K
base-hr.apk 11K
base-hu.apk 11K
base-hy.apk 12K
base-in.apk 17K
base-is.apk 10K
base-it.apk 15K
base-iw.apk 11K
base-ja.apk 11K
base-ka.apk 13K
base-kk.apk 12K
base-km.apk 13K
base-kn.apk 14K
base-ko.apk 18K
base-ky.apk 12K
base-ldpi.apk 2.4M
base-lo.apk 13K
base-lt.apk 11K
base-lv.apk 11K
base-master.apk 3.5M
base-mdpi.apk 2.4M
base-mips.apk 429K
base-mk.apk 12K
base-ml.apk 14K
base-mn.apk 12K
base-mr.apk 13K
base-ms.apk 10K
base-my.apk 14K
base-nb.apk 18K
base-ne.apk 14K
base-nl.apk 11K
base-pa.apk 13K
base-pl.apk 11K
base-pt.apk 23K
base-ro.apk 11K
base-ru.apk 20K
base-si.apk 13K
base-sk.apk 11K
base-sl.apk 11K
base-sq.apk 11K
base-sr.apk 17K
base-sv.apk 10K
base-sw.apk 11K
base-ta.apk 13K
base-te.apk 14K
base-th.apk 13K
base-tl.apk 11K
base-tr.apk 16K
base-tvdpi.apk 2.5M
base-uk.apk 12K
base-ur.apk 12K
base-uz.apk 11K
base-vi.apk 11K
base-x86.apk 483K
base-x86_64.apk 453K
base-xhdpi.apk 2.4M
base-xxhdpi.apk 2.5M
base-xxxhdpi.apk 2.5M
base-zh.apk 27K
base-zu.apk 11K
standalone-arm64_v8a_hdpi.apk 6.8M
standalone-arm64_v8a_ldpi.apk 6.8M
standalone-arm64_v8a_mdpi.apk 6.8M
standalone-arm64_v8a_tvdpi.apk 6.9M
standalone-arm64_v8a_xhdpi.apk 6.8M
standalone-arm64_v8a_xxhdpi.apk 6.9M
standalone-arm64_v8a_xxxhdpi.apk 6.9M
standalone-armeabi_v7a_hdpi.apk 6.8M
standalone-armeabi_v7a_ldpi.apk 6.8M
standalone-armeabi_v7a_mdpi.apk 6.8M
standalone-armeabi_v7a_tvdpi.apk 6.9M
standalone-armeabi_v7a_xhdpi.apk 6.8M
standalone-armeabi_v7a_xxhdpi.apk 6.9M
standalone-armeabi_v7a_xxxhdpi.apk 6.9M
standalone-mips_hdpi.apk 6.8M
standalone-mips_ldpi.apk 6.8M
standalone-mips_mdpi.apk 6.8M
standalone-mips_tvdpi.apk 6.9M
standalone-mips_xhdpi.apk 6.8M
standalone-mips_xxhdpi.apk 6.9M
standalone-mips_xxxhdpi.apk 6.9M
standalone-x86_64_hdpi.apk 6.8M
standalone-x86_64_ldpi.apk 6.8M
standalone-x86_64_mdpi.apk 6.8M
standalone-x86_64_tvdpi.apk 6.9M
standalone-x86_64_xhdpi.apk 6.8M
standalone-x86_64_xxhdpi.apk 6.9M
standalone-x86_64_xxxhdpi.apk 6.9M
standalone-x86_hdpi.apk 6.9M
standalone-x86_ldpi.apk 6.8M
standalone-x86_mdpi.apk 6.8M
standalone-x86_tvdpi.apk 7.0M
standalone-x86_xhdpi.apk 6.9M
standalone-x86_xxhdpi.apk 6.9M
standalone-x86_xxxhdpi.apk 6.9M
Note that all apks are prefixed with base-, since our app only contains the one base module, there is not dynamic feature module.
base-master.apk Contains the code and resources for the base module
base-armeabi_v7a.apk, base-arm64_v8a.apk, etc. ABI configuration splits
base-xhdpi.apk, base-mdpi.apk, etc. Screen density configuration splits
base-ko.apk, base-fr.apk, etc. Language configuration splits
standalone-x standalone splits for that configuration
Generate APKs archive set for connected/specific device
As per Google IO’18 official video, bundletool
has an ability to generate the apk archive set which contains the apks for the connected device only. In this video, they demonstrated two commands, they generate the my_app.apks
apks archive set for just the connected device or as per the specs from provided json.
bundletool build-apks
--bundle=app/build/outputs/bundle/debug/bundle.aab
--output=my_app.apks
--connected-devicebundletool build-apks
--bundle=app/build/outputs/bundle/debug/bundle.aab
--output=my_app.apks
--device-spec=pixel2.jsonError:[BT:0.3.3] Error: Unrecognized flags: --device-spec
com.android.tools.build.bundletool.utils.flags.ParsedFlags$UnknownFlagsException: Unrecognized flags: --device-specUpdate on issue : they're working on these flags and they will be available in the upcoming release.
Sadly, they’re giving me error, I’ve opened the issue under bundletool repo, it may be tool issue or something I’m missing.
Install/Deploy to a connected device
As per my understanding and based on the official documentation it should generate and install the APK from the my_app.apks
archive set which we generated in the previous step. Use device-id
parameter if you have more than 1 device connected.
$ bundletool install-apks --apks=my_app.apks
$ bundletool install-apks --apks=my_app.apks --device-id=<serial-id>Note : When building the APK Set, set the --ks and --ks-key-alias flags to ensure that the APKs are signed. Only signed APKs can be installed on a device. Otherwise, you'll see the `INSTALL_PARSE_FAILED_NO_CERTIFICATES` error.
Note : I’ve tried both commands are and they’re giving me a problem, I’ve opened the issue under bundletool github repo. My guess is it’s related to signing apk.
Extract APKs for a specific device configuration
To extract apks from generated APKs archive set
for a specific device configs, you’ve to provide the device information to the bundletool
command. Either you can create manually .json
file which contains the device specs or get the device specs from the connected device.
bundletool get-device-spec
, this command will give the error for missing required flagoutput
flag. Android developer website doesn’t mention this. Once you’ve the device specs json then usebundletool extract-apks
command to generate apks only for pixel2 device.
$ bundletool get-device-spec --output=pixel2.json$ cat pixel2.json
{
"supportedAbis": ["arm64-v8a", "armeabi-v7a", "armeabi"],
"supportedLocales": ["en-US"],
"screenDensity": 560,
"sdkVersion": 27
}$ bundletool extract-apks
--apks=my_app.apks
--output-dir=pixel2_apks
--device-spec=pixel2.jsonpixel2_apks contains the following output :base-arm64_v8a.apk 434K
base-en.apk 29K
base-master.apk 3.5M
base-xxxhdpi.apk 2.5M
2. Manually create a device specification JSON as per the below example and just apply the same bundletool extract-apks
command with custom json file.
{
"supportedAbis": ["arm64-v8a", "armeabi-v7a"],
"supportedLocales": ["en", "fr"],
"screenDensity": 640,
"sdkVersion": 27
}
Thats’ it, I explored most of the major operations on the bundletool. Just try this tool with your application and see that how much application size you can reduce before uploading to the Google Play Console
Open Questions/Issues
There are some questions and issues after playing with this tool for few hours.
Is there any way to generate final installable apk from bundle or apks archive set?
I want to compare/analyze the two apks: Universal (tradinational) apk vs APK to specific device (for eg. pixel 2 XL). But I didn’t find any way where I can generate apk for a specific device, I can extract the apks as per device configuration but it gives me all different apks. I am looking for a way to generate only one apk which should be for that particular device (for eg : base+en_US+arm64+xxxhdpi = pixel2 XL)
Although, there is a way to install the apk buildtool install-apks
, which I discussed above but it’s giving the error : INSTALL_PARSE_FAILED_NO_CERTIFICATES
Bundle tool "install apks”
command isn’t working
Update : Have to set the --ks
and --ks-key-alias
flags to ensure that the APKs are signed. Only signed APKs can be installed on a device, updated the commands, check the above sections.
bundletool build-apks ` — connected-device` or ` — device-spec` flag/parameter are not working
Update : They’re working on it.
I’ll update this post once the open questions/issues are resolve. Stay tuned for the update. Until then share your questions/thoughts in the comments section. I would be happy to discuss more.