Best Practices
2. Setup and SDK Configuration

Optimizing Bundle Sizes for Android

Ditto SDK Size

The Ditto SDK for Android is a fairly large package. The bulk of the size is made up of native binaries compiled from native Rust code which - unlike Kotlin and Java - need to be compiled for each CPU type (ABI) that the SDK will be used on. We currently support all 4 ABIs supported by the Android NDK. This means that there are 4 native binaries included inside the Android archive .aar file we publish to Maven Central.

Below are various options to reduce the size of your application package. These change aspects of the Ditto SDK as it is included inside your app’s final .apk file.

Publishing to Google Play Store

If you publish your app to the Google Play Store, the easiest way to reduce your app size is to package your app as an Android App Bundle. This format allows Google Play to generate optimized APKs for each device configuration so that users download packages streamlined for their specific device.

Publishing to Private Distribution Channels

If your app is distributed privately to enterprise users via an MDM system, check with your MDM vendor to see whether they integrate with Managed Google Play. If so, they may support Android App Bundles (.aab files) and automatic package optimization.

However, if your MDM system does not support this, there are several options for manually optimizing the size of your app package.

Compress Native Binaries

The native binaries included with the Android Ditto SDK are included in an app’s package in uncompressed form by default. However, setting the useLegacyPackaging value to true in your app’s Gradle configuration will change this to store the binaries in compressed form. Compressed binaries will take up less space and help to reduce the app’s final .apk file size.

In your app’s build.gradle(.kts) file, add the following jniLibs block inside the packaging block.

Gradle (Groovy
Gradle (Kotlin)


Compressed binaries has shown to reduce a simple demo app’s size by 50%.

This useLegacyPackaging build option can also be controlled using the (deprecated)android:extractNativeLibs attribute on the <application> element in the Android manifest. This option used to be available using the android.bundle.enableUncompressedNativeLibs Gradle property.

Caveats

While this useLegacyPackaging option can help to reduce your final .apk file size, it is important to be aware of the side effects of changing this setting and to test your application to understand how it will behave.

  • Increased app launch time as compressed binaries need to be decompressed.
  • Increased app install size as uncompressed binaries are copied into app’s data directory.
  • App updates through the Google Play Store will be larger.

More details

Exclude Unused CPU Architectures

If your app does not need to support all of the different types of CPUs (ABIs) that the Ditto SDK supports, you can configure your build to exclude the unwanted files. There are two main approaches to this. The first is to just exclude the one or more ABIs that you don’t want. The second approach is more extreme and involves building multiple APKs, each with only a single ABI, but it is more complicated to deploy.

Including only a single ABI has shown to reduce a simple demo app’s size by 70%.

In order to use either of these approaches you need to understand the CPU architecture (ABI) of all the devices you will be deploying your app to. Excluding the ABI for a device and then attempting to run the app on a device that uses the missing ABI will cause the Ditto SDK to crash.

Single APK with reduced ABIs

This approach is very simple and just excludes one or more Ditto native binaries from your app package.

In your app’s build.gradle(.kts) file, add an ndk block inside the defaultConfig block like so:

Gradle (Groovy)
Gradle (Kotlin)


The example snippet above adds all 4 ABIs that are contained in the Ditto Android SDK. Reduce this list to just the ABIs of the devices that your app is deployed to. Android devices with Intel CPUs (x86* ABIs) are rare nowadays, but Chromebooks often have these types of CPUs.

For more details about this ndk.abiFilters configuration, see Generate Code for a specific ABI.

Caveats

  • Your app will crash with message "Native library failed to load" if run on a device that uses an ABI excluded using this method.

Separate APKs for each Supported ABI

This is the most involved process but it yields the absolute smallest APK size possible. There are several caveats with this approach but it may be necessary if your deployment system has very limited requirements.

Add the following splits block to your app’s build.gradle(.kts) file inside the android block.

Gradle (Groovy)
Gradle (Kotlin)


The above configuration will generate a separate APK for each ABI listed above in the include configuration. You may remove any ABIs that your environment doesn’t need.

For more details see about the splits.abi Gradle configuration, see Configure multiple APKs for ABIs.

Note that the Google Play Store requires each APK you publish to have a unique versionCode. Check with your MDM system’s requirements as to what their app versioning policies are. A strategy for generating a unique versionCode for each APK is detailed in the Build multiple APKs guide. For more information about this topic, see Assigning version codes.

Caveats

  • Single-ABI APKs need to be carefully deployed to the correct devices via MDM.
  • APK versioning can be more complicated, depending on your MDM system’s requirements.