We know that, on average, 80% of users use only 20% of the features in an application. Suppose you have an application with 30 features (20 MB), and all of your features are in the app module. So, every user will download a 20 MB app, and it will occupy the required storage after installation. However, most of the features are not used by a user. For a specific user, it’s a waste of memory because they may only need 20–30% of the features.

Here we have only one module. Name: app

What would it be like if we could download only the parts of our app that we needed? Consider an eCommerce application with a customer support feature that includes chat and audio call capabilities. To implement this feature, you have used a third-party SDK (e.g., 5 MB).

You know that this feature is not necessary for every user. So, the idea is to exclude the customer support feature from your main app module. When a user taps on the customer support button, they will be able to download this feature from the Play Store. After downloading, the user can use the feature.

Generally, when we start Android development, we work in a monolithic app module. There is only one module in our application, and all features remain in this app module. We upload the .apk or .aab file to the Play Store, and users download our entire application at once.

On the other hand, in a dynamic feature module, we can split our application into a few modules. The user will download the base module (app module) for the first time. After that, they will be able to download other feature modules at runtime.


Suppose you have an eCommerce application. Another feature is customer support (chat & audio-video call). Your main feature is online shopping (app module, for example, its size is 10 MB). However, for some special cases, users need to chat or call customer support. So, you can develop your customer support feature as a dynamic feature module outside the app module (for example, the customer support feature size is 5 MB).

Android dynamic on demand feature module delivery tutorial
Wireframe of Android on Demand dynamic feature module

The first time a user downloads the app, they will get the app module without the customer support feature. So, they will download a 10 MB app. When someone needs to contact customer support, they will click on your specific button and download the customer support module (size 5 MB) from the Play Store on-demand basis. This way, lots of users save an additional 5 MB cost.

That’s the idea of On-Demand Dynamic Feature Delivery Module!

First of all, I have created a new project with the App ID com.hellohasan.hasanerrafkhata. This app is already on the Play Store, and I have developer access on the Google Play Console. The App ID is important here for testing the dynamic feature module. You need to upload the .aab file to the Google Play Store or use Google Play Internal App Sharing. So, make sure you have access to the Play Store for your mentioned Application ID.

After creating the project with my own app ID, I added dependencies in the build.gradle.kts. In some tutorials, I found instructions to add the Google Play Core library. However, I believe it’s enough to add the feature-delivery library for this case.

build.gradle.kts (app module)

dependencies {
// other dependencies


After syncing the project, I encountered the first error!

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:checkDebugDuplicateClasses'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
> Duplicate class android.support.v4.app.INotificationSideChannel found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.app.INotificationSideChannel$Stub found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.app.INotificationSideChannel$Stub$Proxy found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver$Stub found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver$Stub$Proxy found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$1 found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$MyResultReceiver found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$MyRunnable found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)

Go to the documentation to learn how to <a href="d.android.com/r/tools/classpath-sync-errors">Fix dependency resolution errors</a>.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org


To solve this error, I added these two lines in the gradle.properties file.


Create a new module like below:

Creating Dynamic Feature module in Android
Confirmation of creating new dynamic feature module with required configuration

In the above configuration, I have set ‘Do not include module at Install-time (on-demand only)’ because I want to install the dynamic module on demand only. So, my dynamic module will not be included at install time. After creating the translation dynamic module, my project structure looks like the one below:

Now there are two modules in our project. app & translationdynamicmodule

Then, I created a new Activity, CustomerSupportActivity.kt, inside the customer_support module. Android Studio internally changed some configurations in a few different places. If you want to convert any existing feature module into an on-demand dynamic module, you have to configure these manually. Let’s check them!

Changes in build.gradle.kts (app level)

android {
// other configurations

// this line is automatically added by Android Studio
// after creating a new dynamic module
dynamicFeatures += setOf(":customer_support")

AndroidManifest.xml (dynamic module)

<dist:on-demand />

<dist:fusing dist:include="false" />

build.gradle.kts (dynamic module)

plugins {

// others configurations

dependencies {

// other dependencies

build.gradle.kts (Project level gradle)

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.1.4" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
id("com.android.dynamic-feature") version "8.1.4" apply false



Create dynamic module Activity

My dynamic module is named ‘customer_support’ module. However, it’s not my objective to develop a real customer support chat or audio/video call feature in this sample project. For the sake of simplicity, I have included six large images (~18 MB) in CustomerSupportActivity.kt. Using these large images, we will clearly understand the effect of the dynamic module.

Android on demand dynamic feature devlivery module example tutorial
Our Sample dynamic module app UI design

In my app module, I have only one activity — MainActivity.kt. I simply added a banner image of the eCommerce app and then included a Customer Support button. When a user taps on the Customer Support button for the first time, they will see an alert dialog to confirm downloading the dynamic module. Upon clicking the Download button, MainActivity.kt will start downloading the customer_support module from the Google Play Store. After downloading and installing, MainActivity.ktwill receive a few callbacks. Based on those callbacks, the activity will redirect the user to CustomerSupportActivity.kt.

Add a simple button in MainActivity to open the dynamic module Activity. After clicking the button, we will check if the dynamic module is already downloaded. If yes, then open the dynamic module Activity. Otherwise, we will request to download the dynamic module using our utility class.

When our utility class tries to download and completes the process, successfully installing the new module, our app module’s MainActivity will be notified by a callback.


This util file is responsible to handle initialization, checking if a module has already been downloaded and also starting of a dynamic module download with all the possible callbacks. This can be a good idea to have to handle multiple dynamic modules in your app.

There is another Kotlin file for the alert dialog composable widget. You can easily understand the full flow after exploring the entire project. Please check here for the full source code:

I’ve previously mentioned that the customer support module contains six large images (18 MB), resulting in a final .aab file size of 22.5 MB.

Analyse .aab file with base module and customer_support module

So, the size of my main app (base module) is 4.8 MB. After installing this app, you will find a button for downloading the dynamic module. You can then download the dynamic module, which has a size of 17.6 MB, from the Play Store.

Upload your .aab file to Google Play Internal App Sharing and use the URL to install the app from play store.

Internal app sharing google play store to test dynamic feature module
Internal App Sharing of Google Play Console
  1. Make sure the versions of your common dependencies are the same across modules. You might need the same dependencies in the app module as well as dynamic modules. All common library versions should be the same. Otherwise, you may face unwanted build errors.
  2. Ensure that your Java and Kotlin versions are consistent across both the app and other modules.
  3. Check the compatible Kotlin version of the dynamic feature dependency. If your project’s Kotlin version is not compatible with the latest dynamic feature library, consider downgrading the library version (if you are unable to update the Kotlin version immediately).
  4. If any of your third-party libraries or SDKs has NDK configuration internally, you may need to add a few configurations in the app module’s gradle file (under android). I believe this is not directly related to the dynamic feature module. In one of my projects, I had to add this configuration in the gradle file due to a third-party SDK.
ndk {

5. If your modules contain duplicate classes, you may encounter errors during the release APK build. I have faced this type of error:

java.lang.RuntimeException: Duplicate class org.intellij.lang.annotations found in modules …

To fix the error I had to exclude the duplicate module from the project:

// build.gradle.kts (dynamic module)
// this configuration will exclude the duplicate classes from my dynamic module
configurations.implementation {
exclude(group = "org.jetbrains", module = "annotations")

6. If your app module contains flavors in the build.gradle file, ensure that you add those flavor names to your dynamic module’s build.gradle.kts file.

// build.gradle.kts (dynamic module)
android {
flavorDimensions += "version"
productFlavors {
create("dev") {}

create("stage") {}

create("live") {}

There were three product flavors in my app module (dev, stage, live). That’s why I added these three flavors to my dynamic module.

That’s it! I hope you enjoyed the blogs. Feel free to share your feedback in the comment section. Don’t forget to clap! 👏 👏 👏

Let’s connect on LinkedIn.

Source link