How to create a dynamic feature module for your ML Kit App

Dynamic feature module for your ML Kit App

Now that you have learned how to implement Machine Learning in your Apps(if not just turn to ML Kit Tutorial Series), you now agree that ML Kit helps us implement Machine Learning functionalities in just few lines of code. But there is an important point to consider. In the case of on-device models, there could be situations in which your users could be downloading ML models that are large and they would never require. The solution to this problem is to make ur ML features available on-demand with Dynamic Feature Modules. Let's take a look at how we can implement this feature.

What is Dynamic Feature Module?

Let's 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. Dynamic Feature Module helps us do exactly this. It allows 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 required components on demand after they’ve already installed the base APK of your app.

Dynamic delivery

How to create a dynamic feature module?

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

Before creating a dynamic feature module, make sure you have created your app bundle. Refer this post for this. To add a dynamic feature module to your app project using Android Studio, proceed as follows:

  1. Select File > New > New Module from the menu bar. In the Create New Module dialog, select Dynamic Feature Module and click Next.

    Create dynamic feature module

  2. On the Configure your new module screen, give your module a name.

    Create dynamic feature module step 2

  3. On the Configure your new module screen, specify the module title.

    Create dynamic feature module step 3

    • Check the Enable on-demand box if you want the module to be available for on demand downloads.

    • Check the Fusing box 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.

  4. Click Finish and wait for the project to sync.

Your latest Android Studio should automatically manage rest of the necessary settings for you.

Gradle configuration for Dynamic Feature Module

  • Android Studio applies the following Gradle plugin to the module’s build.gradle file:

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

  • No need to include:

    • Signing configurations: App bundles are signed using signing configurations of the base module.
    • The minifyEnabled property: You can enable code shrinking for your entire app project from only the base module’s build configuration.
    • versionCode and versionName: Gradle uses app version information that the base module provides.
  • When your dynamic module is created, it is added to the android.dynamicFeatures property of the base module’s build.gradle file: android { android.dynamicFeatures = [":dynamic-feature-test"] }

  • Additionally, base module’s dependency is added to the dynamic module. implementation project(':app')

Dynamic feature module : AndroidManifest.xml

  <?xml version="1.0" encoding="utf-8"?>
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:dist="http://schemas.android.com/apk/distribution"
      package="com.test.dynamic"
      split="dynamic-feature-test">

    <dist:module
          dist:onDemand="true"
          dist:title="@string/title_test">
          
      </dist:module>

  </manifest>
  • split="split_name" : Defines the name of the module, which your app specifies when requesting an on demand module using the Play Core Library.

  • <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.

  • dist:title="@string / feature_name" : Specifies a user-facing title for the module.

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

How to request on demand Dynamic Feature Module

There are two ways to request on demand Dynamic Feature Module:

  1. Using SplitInstallManager.startInstall()

    When your app requests an on demand module, the Play Core Library employs a “fire-and-forget” strategy in an async fashion.

      SplitInstallManager splitInstallManager =
        SplitInstallManagerFactory.create(context);
    
    SplitInstallRequest request = SplitInstallRequest.newBuilder()
            .addModule("dynamic-module-1")
            .addModule("dynamic-module-2")
            .build();
    
    splitInstallManager.startInstall(request)
        .addOnSuccessListener(sessionId -> { ... })
        .addOnFailureListener(exception -> { ... });
    

  2. Using SplitInstallManager.deferredInstall()

    Request for deferred install is best-effort and you cannot track its progress. So, before trying to access a module you have specified for deferred installation, you should check whether the module has been installed or not.

      splitInstallManager.deferredInstall(Arrays.asList("dynamicModule"));
    

Monitoring the request states of on demand Module

  int mySessionId = 0;
// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener = state -> {
    if (state.status() == SplitInstallSessionStatus.FAILED
       && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
       // Retry the request.
       return;
    }
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.DOWNLOADING:
              int totalBytes = state.totalBytesToDownload();
              int progress = state.bytesDownloaded();
              // Update progress bar.
              break;

            case SplitInstallSessionStatus.INSTALLED:
              //Module is downloaded successfully
              break;
        }
    }
};
// Registers the listener.
splitInstallManager.registerListener(listener);

splitInstallManager.startInstall(request)
    .addOnSuccessListener(sessionId -> { mySessionId = sessionId; })
    .addOnFailureListener(exception -> {
        switch(((SplitInstallException) exception).getErrorCode()) {
            case SplitInstallErrorCode.NETWORK_ERROR:
                //Network Error
                break;
            case SplitInstallErrorCode.MODULE_UNAVAILABLE:
                //Module Not Available
                break;
            ...
     });

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener);





Author:


Ratul Doley
Ratul Doley
Entrepreneur and AI researcher. Currently learning and working on Unsupervised learning and Data Clustering. Professional Android app developer and designer. Updated Nov 15, 2018