Android App Bundle Part-2 : BundleTool

Saurabh Patel
MindOrks
Published in
8 min readMay 21, 2018

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.

  1. 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).
  2. Now, update the Android Gradle Plugin version to 3.2.0-alpha14 or abovein project build.gradle file to use the Android App Bundle feature, build/sync the project.
  3. 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

  1. 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
  2. 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:

  1. Locally using the bundletool command line tool, download this tool from github, It’s open sourced by Google.
  2. 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_aliasesfile.

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 have timber_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-device
bundletool build-apks
--bundle=app/build/outputs/bundle/debug/bundle.aab
--output=my_app.apks
--device-spec=pixel2.json
Error:[BT:0.3.3] Error: Unrecognized flags: --device-spec
com.android.tools.build.bundletool.utils.flags.ParsedFlags$UnknownFlagsException: Unrecognized flags: --device-spec
Update 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.

  1. bundletool get-device-spec , this command will give the error for missing required flag output flag. Android developer website doesn’t mention this. Once you’ve the device specs json then use bundletool 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.json
pixel2_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.

Thank you for using your precious time to reading this article, If you liked the article, Clap your 👏👏 to say “thanks!” and help others find this article by sharing it. Also, you can follow me on Twitter or Medium!! #KeepLearning #AlwaysLearning📚

--

--