Configure your project for Dynamic Delivery

For your app to support Google Play’s Dynamic Delivery, you must first configure your project to build an Android App Bundle—a single artifact that includes all your app’s compiled code and resources, but defers APK generation and signing to Google Play.

Google Play uses your app bundle to generate and serve optimized APKs for each user’s device configuration, so they download only the code and resources they need to run your app. You no longer have to build, sign, and manage multiple APKs, and users get smaller, more optimized downloads.

Most app projects won’t require much effort to build app bundles that support serving optimized APKs using Dynamic Delivery. For example, if you already organize your app’s code and resources according to established conventions, you can build signed Android App Bundles using Android Studio and upload them to Google Play. Dynamic Delivery then becomes an automatic benefit.

Additionally, you can add dynamic feature modules to your app project and include them in your app bundle. Through Dynamic Delivery, your users can then download and install your app’s dynamic features on demand. However, creating on demand modules require more effort and possible refactoring of your app. So, consider carefully which of your app’s features would benefit the most from being available to users on demand.

The easiest way to start configuring your project for Dynamic Delivery is by using the latest version of Android Studio. So, if you haven’t already done so, download Android Studio 3.2 Canary now.

Dynamic Delivery with split APKs

A fundamental component of Dynamic Delivery is the split APK mechanism available on Android 5.0 (API level 21) and higher. Split APKs are very similar to regular APKs—they include compiled DEX bytecode, resources, and an Android manifest. However, the Android platform is able to treat multiple installed split APKs as a single app. That is, you can install multiple split APKs that have access to common code and resources, and appear as one installed app on the device.

The benefit of split APKs is the ability to break up a monolithic APK—that is, an APK that includes code and resources for all features and device configurations your app supports—into smaller, discrete packages that are installed on a user’s device as required.

For example, one split APK may include the code and resources for an additional feature that only a few of your users need, while another split APK includes resources for only a specific language or screen density. Each of these split APKs is downloaded and installed when the user requests it or it’s required by the device.

The following describes the different types of APKs that may be installed together on a device to form your full app experience. You’ll learn how to configure your app project to support these APKs in later sections of this page.

  • Base APK: This APK contains code and resources that all other split APKs can access and provides the basic functionality for your app. When a user requests to download your app, this APK is downloaded and installed first. That’s because only the base APK’s manifest contains a full declaration of your app’s services, content-providers, permissions, platform version requirements, and dependencies on system features. Google Play generates the base APK for your app from your project’s base module.
  • Configuration APKs: Each of these APKs includes native libraries and resources for a specific screen density, CPU architecture, or language. When a user downloads your app, their device downloads and installs only the configuration APKs that target their device. Each configuration APK is a dependency of either a base APK or dynamic feature APK. That is, they are downloaded and installed along with the APK they provide code and resources for. Unlike the base and dynamic feature modules, you don't create a separate module for configuration APKs. If you use standard practices to organize alternative, configuration-specific resources for your base and dynamic feature modules, Google Play automatically generates configuration APKs for you.
  • Dynamic feature APKs: Each of these APKs contains code and resources for a feature of your app that is not required when your app is first installed. That is, using the Play Core Library, dynamic APKs may be installed on demand after the base APK is installed on the device to provide additional functionality to the user. For example, you may have a chat app that downloads and installs the ability to capture and send photos only when the user requests to use that functionality. Because dynamic features may not be available at install time, you should include any common code and resources in the base APK. That is, your dynamic feature should assume that code and resources of only the base APK are available at install time. Google Play generates dynamic feature APKs for your app from your project’s dynamic feature modules.

Consider an app with three dynamic feature modules and support for multiple device configurations. Figure 1 below illustrates what the dependency tree for the app’s various APKs may look like. Note that the base APK forms the head of the tree, and all other APKs depend on the base APK. (If you're curious about how the modules for these APKs are represented in an Android App Bundle, see The Android App Bundle format.)

Figure 1. Dependency tree for an app served using split APKs.

Keep in mind, you don’t need to build these APKs yourself—Google Play does it for you using a single signed app bundle you build with Android Studio. To learn more about the app bundle format and how to build one, go to Build, deploy, and upload Android App Bundles.

Devices running Android 4.4 (API level 20) and lower

Because devices running Android 4.4 (API level 20) and lower don’t support downloading and installing split APKs, Google Play instead serves those devices a single APK, called a multi-APK, that’s optimized for the device's configuration. That is, multi-APKs represent your full app experience but do not include unneccessary code and resources—such as those for other screen densities and CPU architectures.

They do, however, include resources for all languages that your app supports. This allows, for example, users to change your app's preferred language setting without having to download a different multi-APK.

Multi-APKs do not have the ability to later download dynamic features modules on demand. To include a dynamic module in this APK, you must either disable On-demand or enable Fusing when creating the dynamic feature module.

Keep in mind, with Dynamic Delivery, you no longer have to build, sign, upload, and manage APKs for each device configuration your app supports. You still build and upload only a single app bundle for your entire app and Google Play takes care of the rest for you. So whether or not you plan to support devices running Android 4.4 or lower, Dynamic Delivery provides a flexible serving mechanism for both you and your users.

The base module

Most app projects won’t require much effort to support Dynamic Delivery. That’s because the module that includes code and resources for your app’s base APK is the standard app module, which you get by default when you create a new app project in Android Studio. That is, the module that applies the app plugin below to its build.gradle file provides the code and resources for the base functionality of your app.

// The standard application plugin creates your app's base module.
apply plugin: 'com.android.application'

If you are concerned with reducing your app’s initial download size, it’s important to keep in mind that all code and resources included in this module are included in your app’s base APK.

In addition to providing the core functionality for your app, the base module also provides many of the build configurations and manifest entries that affect your entire app project. For example, signing for your app bundle is determined by information you provide for the base module, and versioning of all your app’s APKs are specified on the versionCode attribute in your base module’s manifest. Other important aspects of the base module are described below.

The base module manifest

The manifest for your app’s base module is similar to that of any other app module. However, if you are considering adding dynamic feature modules to your app project, there are some aspects to the manifest in the base APK that you should keep in mind:

  • Because the base APK is always installed first, it should provide the main entry point for your app. That is, it should declare an activity with the following intent filter:

    <intent-filter>
         <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    
  • When downloading dynamic feature modules on demand, devices running Android 6.0 (API level 23) and lower require the app to restart before completing installation of the new modules. However, if you want to be able to access the downloaded module’s code and resources immediately after it’s downloaded, you should include support for the SplitCompat library in your manifest. To learn more, read Access code and resources from downloaded modules.

  • Similarly, on devices running Android 6.0 (API level 23) and lower, apps need to restart before the platform can apply new manifest entries. So, if you require certain permissions or services as soon as a dynamic feature module is downloaded, consider including them in the manifest of your base module.

The base module build configuration

For most existing app projects, you don’t need to change anything in your base module’s build configuration. However, if you are considering adding dynamic feature modules to your app project, there are some aspects to the base module’s build configuration that you should keep in mind:

  • App signing: You don’t need to include signing information in the build configuration file unless you want to generate your app bundle from the command line. However, if you do include signing information, you should include it in only the base module’s build configuration file. For more information, see Configure Gradle to sign your app.
  • Code shrinking: If you want to enable code shrinking for your entire app project (including its dynamic feature modules), you must do so from the base module’s build.gradle file. That is, you can include custom ProGuard rules in a dynamic feature module, but the minifyEnabled property in dynamic feature module build configurations is ignored.
  • The splits block is ignored: When building an app bundle, Gradle ignores properties in the android.splits block. If you want to control which types of configuration APKs your app bundle supports, instead use android.bundle to disable types of configuration APKs.
  • App versioning: The base module determines the version code and version name for your entire app project. For more information, go to the section about how to Manage app updates.

Disable types of configuration APKs

By default, when you build an app bundle, it supports generating configuration APKs for each set of language resources, screen density resources, and ABI libraries. Using the android.bundle block in your base module’s build.gradle file, as shown below, you can disable support for one or more types of configuration APKs:

android {
    // When building Android App Bundles, the splits block is ignored.
    splits {...}

    // Instead, use the bundle block to control which types of configuration APKs
    // you want your app bundle to support.
    bundle {
        language {
            // Specifies that the app bundle should not support
            // configuration APKs for language resources. These
            // resources are instead packaged with each base and
            // dynamic feature APK.
            enableSplit = false
        }
        density {
            // This property is set to true by default.
            enableSplit = true
        }
        abi {
            // This property is set to true by default.
            enableSplit = true
        }
    }
}

Manage app updates

With Android App Bundles and Dynamic Delivery, you no longer have to manage version codes for multiple APKs that you upload to Google Play. Instead, you manage only one version code in the base module of your app, as shown below:

// In your base module build.gradle file
android {
    defaultConfig {
        …
        // You specify your app’s version code only in the base module.
        versionCode 5
        versionName "1.0"
    }
}

After you upload your app bundle, Google Play uses the version code in your base module to assign the same version code to all the APKs it generates from that bundle. That is, when a device downloads and installs your app, all split APKs for that app share the same version code.

When you want to update your app with new code or resources, you must update the version code in your app’s base module, and build a new, full app bundle. When you upload that app bundle to Google Play, it generates a new set of APKs based on the version code the base module specifies. Subsequently, when users update your app, Google Play serves them updated versions of all APKs currently installed on the device. That is, all installed APKs are updated to the new version code.

Download additional configuration APKs

One exception to the update flow described above is when an installed app requires additional configuration APKs. Consider a user who changes their default system language after already downloading your app. If your app supports that language, the device requests and downloads additional configuration APKs for those language resources from Google Play. However, this type of update to your app does not change its version code, so the device downloads and installs only the configuration APKs that it requires.

Dynamic feature modules

Dynamic feature modules allow you to separate certain features and resources from the base module of your app and include them in your app bundle. Through Dynamic Delivery, users can later download and install those components on demand after they’ve already installed the base APK of your app.

For example, consider a text messaging app that includes functionality for capturing and sending picture messages, but only a small percentage of users send picture messages. It may make sense to include picture messaging as a downloadable dynamic feature module. That way, the initial app download is smaller for all users and only those users who send picture messages need to download that additional component.

Keep in mind, this type of modularization requires more effort and possibly refactoring your app’s existing code, so consider carefully which of your app’s features would benefit the most from being available to users on demand. Android App Bundles provide some additional options that help you transition your app towards supporting fully on demand features. These options are described later in this section.

In general, dynamic feature modules are organized just like regular app modules. They provide their own code, resources, and assets in the directories you would expect. However, there are some additional attributes in the manifest file and properties in the build configuration that allow dynamic feature modules to enable downloading them on demand.

This section explains those differences and how to create dynamic feature modules using Android Studio.

Create a dynamic feature module

The easiest way to create a new dynamic feature module is by using the latest Canary version of Android Studio. Because dynamic feature modules have an inherent dependency on the base app module, you can add them only to existing app projects.

To add a dynamic feature module to your app project using Android Studio, proceed as follows:

  1. If you haven’t already done so, open your app project in the IDE.
  2. Select File > New > New Module from the menu bar.
  3. In the Create New Module dialog, select Dynamic Feature Module and click Next.
  4. In the Configure your new module section, complete the following:
    1. Select the Base application module for your app project from the dropdown menu.
    2. Specify a Module name. The IDE uses this name to identify the module as a Gradle subproject in your Gradle settings file. When you build your app bundle, Gradle uses the last element of the subproject name to inject the <manifest split> attribute in the dynamic feature module’s manifest.
    3. Specify the module’s package name. By default, Android Studio suggests a package name that combines the root package name of the base module and the module name you specified in the previous step.
    4. Select the Minimum API level you want the module to support. This value should match that of the base module.
  5. Click Next.
  6. In the Configure On-Demand Options section, proceed as follows:

    1. Specify the Module title using up to 50 characters. The platform uses this title to identify the module to users when, for example, confirming whether the user wants to download the module. For this reason, your app’s base module must include the module title as a string resource, which you can translate. When creating the module using Android Studio, the IDE adds the string resource to the base module for you and injects the following entry in the dynamic feature module’s manifest:

      <dist:module
          ...
          dist:title="@string/title_dynamic_feature">
      </dist:module>
      
    2. Check the box next to Enable on-demand if you want the module to be available for on demand downloads. If you do not enable this option, the dynamic feature is available when a user first downloads and installs your app. Android Studio injects the following in the module’s manifest to reflect your choice.

      <dist:module
          ...
          dist:onDemand="true">
      </dist:module>
      
    3. Check the box next to Fusing if you want this module to be available to devices running Android 4.4 (API level 20) and lower and include it in multi-APKs. This option is available only if you checked the box next to Enable on-demand in the previous step. That means you can enable on demand behavior for this module and disable fusing to omit it from devices that don’t support downloading and installing split APKs. Android Studio injects the following in the module’s manifest to reflect your choice.

      <dist:module
          ...
          <dist:fusing dist:include="true" />
      </dist:module>
      
  7. Click Finish.

After Android Studio finishes creating your module, inspect its contents yourself from the Project pane (select View > Tool Windows > Project from the menu bar). You should quickly notice that the default code, resources, and organization are similar to that of the standard app module.

Dynamic feature module build configuration

When you create a new dynamic feature module using Android Studio, the IDE applies the following Gradle plugin to the module’s build.gradle file.

// The following applies the dynamic-feature plugin to your dynamic feature module.
// The plugin includes the Gradle tasks and properties required to configure and build
// an app bundle that includes your dynamic feature module.

apply plugin: 'com.android.dynamic-feature'

Many of the properties available to the standard application plugin are also available to your dynamic feature module. The following sections describe the properties you should and shouldn’t include in your dynamic feature module’s build configuration.

What not to include in the dynamic feature module build configuration

Because each dynamic feature module depends on the base module, it also inherits certain configurations. So, you should omit the following in the dynamic feature module’s build.gradle file:

  • Signing configurations: App bundles are signed using signing configurations that you specify in the base module.
  • The minifyEnabled property: You can enable code shrinking for your entire app project from only the base module’s build configuration. So, you should omit this property from dynamic feature modules. You can, however, specify additional ProGuard rules for each dynamic feature module.
  • versionCode and versionName: When building your app bundle, Gradle uses app version information that the base module provides. You should omit these properties from your dynamic module’s build.gradle file.

Establish a relationship to the base module

When Android Studio creates your dynamic feature module, it makes it visible to the base module by adding the android.dynamicFeatures property to the base module’s build.gradle file, as shown below:

// In the base module’s build.gradle file.
android {
    ...
    // Specifies dynamic feature modules that have a dependency on
    // this base module.
    dynamicFeatures = [":dynamic-feature", ":dynamic-feature2"]
}

Additionally, Android Studio includes the base module as a dependency of the dynamic feature module, as shown below:

// In the dynamic feature module’s build.gradle file:
...
dependencies {
    ...
    // Declares a dependency on the base module, ':app'.
    implementation project(':app')
}

Specify additional ProGuard rules

Although only the base module’s build configuration may enable code shrinking for your app project, you can provide custom ProGuard rules with each dynamic feature module using the consumerProguardFiles property, as shown below.

android.buildTypes {
     release {
         // You must use the following property to specify additional ProGuard
         // rules for dynamic feature modules.
         consumerProguardFiles 'proguard-rules-dynamic-features.pro'
     }
}

Note that these ProGuard rules are merged with those from other modules (including the base module) at build time. So, while each dynamic feature module may specify a new set of rules, those rules apply to all modules in the app project.

Dynamic feature module manifest

When creating a new dynamic feature module using Android Studio, the IDE includes most of the manifest attributes that the module requires to behave like a dynamic feature. Additionally, some attributes are injected by the build system at compile time, so you needn’t specify or modify them yourself. The following table describes the manifest attributes that are important to dynamic feature modules.

Attribute Description
<manifest
...
This is your typical <manifest> block.
xmlns:dist="http://schemas.android.com/apk/distribution" Specifies a new dist: XML element that’s described further below.
split="split_name" Defines the name of the module, which your app specifies when requesting an on demand module using the Play Core Library.

When Android Studio builds your app bundle, it includes this attribute for you. So, you should not specify or modify this attribute yourself.

How Gradle determines the value for this attribute:

By default, when you create a dynamic feature module using Android Studio, The IDE uses what you specify as its Module name to identify the module as a Gradle subproject in your Gradle settings file.

When you build your app bundle, Gradle uses the last element of the subproject path to inject this manifest attribute in the module’s manifest. For example, if you create a new dynamic feature module in the MyAppProject/features/ directory and specified "dynamic_feature1" as its Module name, the IDE adds ':features:dynamic_feature1' as a subproject in your settings.gradle file. When building your app bundle, Gradle then injects <manifest split="dynamic_feature1"> in the module’s manifest.

android:isFeatureSplit="true | false"> Specifies that this module is a dynamic feature module. Manifests in the base module and configuration APKs either omit this attribute or set it to false.

When Android Studio builds your app bundle, it includes this attribute for you. So, you should not specify or modify this attribute manually.

<dist:module This new XML element defines attributes that determine how the module is packaged and distributed as APKs.
dist:onDemand="true | false" Specifies whether the module should be available as an on demand download. That is, if this attribute is set to true, the module is not available at install time, but your app may request to download it later.

If this attribute is set to false, the module is included when the user first downloads and installs your app.

To learn more about on demand downloads, read about Download modules with the Play Core Library.

dist:title="@string/feature_name" Specifies a user-facing title for the module. For example, the device may display this title when it requests download confirmation.

You need to include the string resource for this title in the base module’s module_root/src/source_set/res/values/strings.xml file.

<dist:fusing dist:include="true | false" />
</dist:module>
Specifies whether to include the module in multi-APKs that target devices running Android 4.4 (API level 20) and lower.

Additionally, when you use bundletool to generate APKs from an app bundle, only dynamic feature modules that set this property to true are included in the universal APK.

Deploy your app

While you’re developing your app with support for Dynamic Delivery, you can deploy your app to a connected device like you normally would by selecting Run > Run from the menu bar (or by clicking Run in the toolbar).

If your app project includes one or more dynamic feature modules, you can choose which dynamic features to include when deploying your app by modifying your existing run/debug configuration as follows:

  1. Select Run > Edit Configurations from the menu bar.
  2. From the left panel of the Run/Debug Configurations dialog, select your desired Android App configuration.
  3. Under Dynamic features to deploy in the General tab, check the box next to each dynamic feature module you want to include when deploying your app.
  4. Click OK.

By default, Android Studio doesn’t use app bundles to deploy your app—it builds and pushes APKs to your device that are optimized for deployment speed, rather than APK size. To configure Android Studio to instead build and deploy APKs from an app bundle, go to Deploy your app from an app bundle.