Configuring your app for Play Feature Delivery

Ben Weiss
Android Developers
Published in
5 min readNov 10, 2020

--

This article is available as a video and linked at the end of the post.

The Android App Bundle is the publishing format for Android Applications in the Modern Android Development world.

Apps using it have seen an average file size savings of 15 % on user’s devices compared to a universal apk. You can benefit from these savings and streamlined releases without having to make any changes to your app’s code, simply by switching to an Android App Bundle.

To learn more about how to build your first Android App Bundle, read the previous post in this series.

But you can take matters into your own hands and modularize as well as optimize installation even further using Play Feature Delivery.

Why should you care about a modularized app and Play Feature Delivery?

A modularized app will create clearly defined boundaries between separate parts of your application. This has benefits on multiple levels.

In many cases, you only need to rebuild parts of your app. That allows build times to decrease. With decreased build times and clear boundaries between modules, engineering velocity is likely to improve.

Also, on the Google Play Store we have seen that

reducing the initial download size of an app by 3 MB can result in a jump in installations of about 1 %.

In this post you will learn about features of Android App Bundles that enable Play Feature Delivery. These features empower you to decrease your app’s size even further. And I will introduce you to the APIs you can use to deliver features conditionally or on demand as well as multiple configuration options.

You can use Android Studio to take you through the “New Module” flow. But we are going to look under the hood when you go through the flow and how you can change your configuration later on.

Establishing the baseline

When starting to modularize an app using feature modules your baseline is install time modules. Here you are already able to reap in benefits such as improved build speed and engineering velocity.

The base configuration for an install-time module looks like this:

The most important parts are the distribution namespace, xmlns:dist="http://schemas.android.com/apk/distribution" and the delivery configuration attribute to be set to install-time.

A module configured like this will be installed on the device when the initial installation is requested.

By default every install-time module will be fused into the base module, which makes them non-removable. If you want to be able to remove an install-time module later on, all you have to do is set the removable attribute’s value to true.

Uninstalling modules can be useful for tutorials, sign up flows or other modules with a large footprint that you want to have available during initial installation but that are likely to be of no further use once the flow is complete.

We provide the PlayCore API to request installation and uninstallation of modules and I will cover it later in this post.

Note for devices before Android Lollipop 5.0

The mechanism that enables feature module installation requires devices to run on Android Lollipop 5.0 or newer. For older versions of Android, the feature module’s content can be placed into the base apk. To enable this explicitly you need to set fusing’s include value to true inside the module tag.

<dist:fusing dist:include=”true”>

Conditional delivery set up

Next to install time delivery, conditional delivery is another way to request a feature module. Installation can depend on either the device’s API level, user’s country or a device’s features.

This is what a fully configured AndroidManifest can look like.

Not all of these conditions will have to be set and it is unlikely that you will have to use all of them in a single module. Let’s go through them step by step.

To set up conditional delivery, add the dist:conditions tag.

You can then declare lower and upper API level bounds by using min-api and max-api.

These are helpful when you have a specific module for certain API levels.

Also, each uses-feature element from the AndroidManifest can be used as an install condition. Using the device-feature attribute, you can make sure a feature is only delivered to a user when their device has the technical capabilities to use a feature.

While, by default each user is able to download every feature wherever an app is available, you can choose to make certain features available only for certain countries. This is a great way to localize your app.
To do so, add the user-countries tag and then the two letter country code for a given country.

When you want a feature to not be available in a specific set of countries, make sure to set dist:exclude=”false”. If you want a feature to be only available set the value to true.

Modules without code

Sometimes, all you have is a large asset that you want to deliver to users, such as a TensorFlow model. In cases where you have no code associated with a feature module, make sure that you set hasCode to false in the module’s AndroidManifest file.

<application android:hasCode="false" />

This will let the compiler know that no dex file should be created.

Missing to set hasCode to false when there is no code in a module will result in runtime exceptions.

On demand configuration

To take the exact installation time into your own hands, you can make use of on-demand installation. That means, you can call an API to install the module after the app has been downloaded and installed on the user’s device.
Using on-demand installation saves initial download time and size.

In the AndroidManifest, you will have to set the delivery option to on-demand. Then you can use the PlayCore API to download, install and uninstall modules within your app’s flow.

I recommend you take a look at the PlayCoreKtx sample on GitHub as well as watch the video linked below for a detailed introduction into the on demand delivery section of Play Feature Delivery.

Happy modularizing!

And here’s the video version of this post

--

--