Dynamic feature modules. Is it worth the effort?

Rishabh Khemka
Bobble Engineering
Published in
5 min readAug 3, 2020

Have you been lately searching for tricks to reduce your app size? I am pretty sure you must have come around the term “Dynamic feature modules”. Let’s see how can one benefit from them. Or can one?

How does the app size matters?

App size is one of the vital metrics that an android developer strives to keep low, especially after Nov 2017 when Play Store decided to show app size when showcasing the apps on its platform. Since then Google has also been providing innovative ways to help developers. In 2018, they launched app bundle and dynamic modules. App bundles are no doubt a life saviour when you are out of ideas to squeeze out some extra size. We at Bobble.ai reduced our app size by 19% just by switching over to app bundles. But, what about dynamic delivery?

What are Dynamic feature modules?

The idea of dynamic feature modules is similar to à la carte where the consumer (user in our case) is served only those services which he/she demands. The main motive behind dynamic feature modules was to stop bloating user’s device from giving them features that they don’t even require.

So, after reducing 19% of our app size, we decided to dive into dynamic modules and see if we can further improve our app size. I won’t be discussing on how to start with dynamic modules, rather how it works internally, its limitations, and how to decide if you need dynamic modules.

How does dynamic app modules work?

Let us dive deep to see how dynamic app modules work. By default, when a app is installed, all its code(dex files and native libs) and resources(drawable, strings, values, etc) are placed at /data/app/<package-name>/ . Code and resources here are directly accessible to the system. But, when a dynamic module is downloaded, it’s code and resources are placed at your apps internal storage — /data/data/<package-name>/files/splitcompat/<version-code>/. Code and resources placed here are NOT ACCESSIBLE to the system!

LEFT-data/data/com.touchtalent.bobbleapp RIGHT-data/app/com.touchtalent.bobbleapp/files/splitcompat

This is why, we need to call SplitCompat.install() in Application class and SplitCompat.installActivity() in module activities. These function calls are responsible for emulating the dynamic modules as if they were a part of the app. Without them, the system won’t be able to recognise the dynamic modules. Since we have seen how it works, let’s discuss its limitations.

Limitations (with few workarounds)

  1. The dynamic module feature only works with app bundles and are not included if you build an apk. Yet, if you want to distribute your app via apk (direct apk from a website or other app stores) including the dynamic module, you need to build your project via command line —
    ./gradlew :app:packageDebugUniversalApk
    But this will disable your apk splits(older alternative to app bundles) and you may see a sudden a spike in app size.
  2. If your dynamic feature modules include activities or services which interact with system components, such as launcher shortcut icons, dynamic shortcuts, or notifications, you may have a hard time customising your app to support these. This is because these system components are unable to access resources like icons from dynamic modules. They need to be explicitly moved to the base module instead of dynamic module, which in turn defeats the purpose of modularising.
  3. It is not easy to implement a direct entry point to the dynamic module like a launcher icon. This is because the manifest of the base module and dynamic feature module is merged at build time. Suppose you include an activity X in your dynamic feature module marked with launcher category. This information is delivered with base apk and not dynamic module apk. Therefore the launcher will know that there exists an activity X (because the manifest was merged) and will showcase it. But, when the user clicks on it, a crash will happen if the module isn’t yet downloaded.
    This can be prevented by enabling/disabling the component from the base module. But this can be hard to implement if you want to use deferredInstall(), as it becomes hard to track whether the module has been installed.
    P.S: Google is trying to make this easier with Navigation framework.
  4. Code from dynamic modules is not directly accessible to the base module. This brings in the need of Reflection class, which is still a nightmare for many developers. Special care has to be taken with code obfuscation. Code refactoring and maintainability also becomes hard.
  5. Testing dynamic modules are very difficult since it requires to be deployed on the Play Store. Yes, bundletool does have a local-testing option but it’s not enough for your app to be ready for rollout. Testing via Google Play Internal app sharing is not as efficient as deploying it directly from Android Studio/command line. It is also possible that you may not have set up your app on Play console yet or probably you don’t have access to the internal app sharing(if you are not admin of the play console account).
  6. Resources from Dynamic modules can be accessed only via Application level context because that is where the emulation took place. Using other context won’t throw any exception but will give you unwanted and unexpected behaviour. So, be careful with them.
  7. Android Studio’s resource shrinker, (shrinkResources=true) is NOT compatible with dynamic modules. You may have to manage those changes yourself, if you have been benefiting from them till now. However, this may not be the case when Android studio 4.2 rolls out.
  8. Last but not the least, if your dynamic feature module size exceeds 10MB, Play Store pops up for a confirmation from the user when the download is initiated, which definitely adds to friction in your elegantly designed UI and UX.

How should I decide?

It is undoubtedly, a very hard decision whether or not to use dynamic feature modules. You are now aware of the hardships related to dynamic feature modules and how they can affect you. Analysing the flowchart below may help you to decide if you need dynamic feature modules in your app.

Flowchart to decide whether or not to use dynamic modules.

Bonus - Examples where dynamic modules may be well suited

  1. If you want to run machine learning models locally on the device, you may need to add machine learning library like TensorFlow Lite. Libraries like this along with the ML models can increase initial download size by a great extent.
  2. Imagine an app for managing some accounts. You may optionally provide your users with the option to export their data as an excel file. For that, you may want to use a library like Apache POI. Dynamic modules can be a good option in such scenarios.

Conclusion

Dynamic modules at first may seem to be an amazing concept, but it is very crucial to carefully analyse several aspects of an app before using them. Even after 2 years of launch, it still needs numerous changes for the developers to benefit from them and pass the same to users.

--

--

Rishabh Khemka
Bobble Engineering

I like to write about things that I was searching for some time back but couldn’t find it then.