Dynamic feature modules

Things to note before jumping in to implement dynamic feature modules in the project.

Google Play’s Dynamic Delivery, one of the App Bundle features, helps developers in reducing the app size at the time of installation and thus increase the user onboarding rate. Then based on the stages or levels of the user, they can be exposed to certain features that can be downloaded on demand. 

This certainly is a major driving factor to implement DFM and the internet will of course help you with the blogs and videos on how to do that. But, before jumping straight to the implementation, let’s note down certain caveats of the process & the problems which will come up.

1. Modularise first, dynamise later

If you are dealing with some legacy code in your organization and that is a kind of big monolithic code, then you should plan to break it into modules—feature-wise & utility-wise. And then, you can convert the relevant feature modules into DFMs. However, be careful in choosing for conversions. As a tip, try to avoid download until a user has completed a stage or level in your app.

2. DFM is different from the normal module

You know that the normal modules are independent of the app module and thus can be plugged into any app as it is. But, the issue with DFMs is that they depend on the app module to work i.e. reversed. This means that you can’t access the methods from the DFMs into the app module at compile-time. One obvious use-case will be launching an activity of DFM from the app module. You will have to use the name of the class as a string and you can’t use .class notation like:

val intent = Intent()
intent.setClassName(applicationId,"com.myapp.DfmActivity")
startActivity(intent)

Another issue with this is that code from the app module is accessible to the DFM and thus, you might end up using the methods from the app module in the DFM and thus destroying the plug & play behavior. 

3. Another app like behaviour

When you are done converting the module to DFM, and after running the app from the android studio, you will get ClassNotFoundException when launching the DfmActivity(Main activity of the DFM). This is because you might have selected the configuration to install the only app. So, the DFM is not installed. Thankfully, this is easy to solve. Just edit the run configuration, and select your DFM too to install it with the app.
If you will check the logs, you will get to know that DFM is installed as another apk and thus it behaves as a different app. This means that you have to take care of the possible UX issues that may come up while interacting.

One example: When you are on DfmActivity and then a call comes up, or if you switch to any other app for some reason, and then resume to the DfmActivity again, you will see everything is normal. But, now when you press the back button, you will see you are thrown out of your app instead of the activity which launched DfmActivity. Handling this particular scenario requires you to relaunch the calling activity, and yes be careful about the launch modes.

4. Giving out builds for QA testing

After getting the apk from the android studio by clicking ‘build apk’ & installing that apk, you will find on the launch of DfmActivity will result in the ClassNotFoundException. Why? Because the DFM apk is not generated this way. And as such, there is no direct way to install DFM over the main apk.


There are two ways to solve this :
1. Internal app sharing: Instead of apk, build the app bundle, upload that to internal sharing and share the link with the QA team. One problem with this approach is that if you have different applicationID for dev and pre-prod environments, you will have to create different applications on google play console for the different environments1
2. Bundletool: Generate the app bundle and extract universal apk with the help of bundletool. The universal apk is meant to work on all devices irrespective of the configurations. If you have a continuous delivery(CD) setup done already, it is recommended to install the bundletool at the CD server too, and have it as part of the build process to get the universal apk as artifacts only.

5. Testing on universal APK is not sufficient

Universal apk is just a solution for the above issue and to avoid any blockages in testing the business logic of the app. Since universal apk is loaded with all configurations, it is not equivalent to what user downloads from the play store. Sometimes, because of the conflicting names, the app resources are stripped from DFMs resulting in ResourceNotFound exception. This doesn’t appear in universal apk.
Debugging such issues is also a pain as it may involve uploading the builds to internal app sharing again and again.

Testing via internal app sharing, though tedious, is better in that case.
Another thing to note here is that, if your DFM is having WebView anywhere in the flow then, it may also result in the crash. This is pretty weird and is also acknowledged as a bug on the issue tracker.

So, do we hate DFMs?

No, dynamic delivery is a good offering from the Android team, despite its challenges. It is definitely the future of the app ecosystem. But, since most of the articles only tell about the benefits & tutorials of the implementation, having prepared for the above-mentioned issues will help the developers.