Just recently we have one project which has 2 apps, one for consumer and the other one is for partner. During planning, we decided that: the app for consumer should be as small as possible in size. Thus we follow guide from Google using dynamic feature modules.
The grand design of the app architectures is of course modularisation, and each module should be able to fit in both apps. After some discussions, we decided the following should be presented in our architectures:
Data
, Domain
and View Model
and can be used in either dynamic feature modules or main app,Fragment
and Activity
,Common UI
and Common Code
to be used both in Dynamic Feature Modules and both main apps,Below are full diagrams
Diagram C, D, and B are standard android library modules, which means we can easily import them on the main app and use it directly, however diagram A and B is a bit different, you can’t access feature modules from the main app, it works the other way around.
In this article, we’re going to focus more on part A and B and how we can reuse feature modules in both apps.
As mentioned above, the way Google design dynamic feature module is different than standard library, as mentioned here:
In the standard project structure that uses libraries, the base
com.android.application
module depends on
com.android.library
modules, which means you can use any classes defined in the library from the base module freely.With DFMs however, the base
com.android.application
is a dependency ofcom.android.dynamic-feature
modules, which means you can use any classes defined in the base module and its libraries in the DFM, but you can’t reference any code defined in the DFM from the base application at compile time.
Now this is fine if you only have one app, however in our project, this DFM design is forcing us to do something creative 😌. A naive approach to this problem is to copy Activity/Fragment from consumer project and paste in partner app, but that’s not what we want, right?
right!
Make sure when creating dynamic feature module, avoid creating dependency with main apps as much as possible, by doing the followings:
android.R
on main apps, if use by both, put it inside Common UI module,BuildConfig.ApplicationID
as an intent to dynamic feature module instead of using it directly. BuildConfig.ApplicationID
are mostly use to call another feature module,AndroidManifest.xml
.idea
every time you change project from consumer to partner app,Let’s try by creating first app called consumer
, create new android module called common
, and dynamic feature module called forgotpassword
Now open forgotpassword feature module’s manifest, and make sure you put dist:onDemand=”true”
If we run the app through internal app sharing, we will see the downloading process of feature module like this:
Note: running the app directly from Android Studio will not show “downloading module” screen. You have to use internal app sharing to test dynamic feature module.
So far so good, now close the consumer app, and let’s create another app, called partner
, update settings.gradle
to include common
and forgotpassword
feature
Next, we also update partner’s build.gradle
similar to consumer’s build.gradle
Pay attention to include ‘:app’
in partner’s settings.gradle, my recommendation is that you should use the same name as in consumer app, but if something or other things force you to use different name, don’t worry you still can reuse the module which i’ll explain later.
Now if you check forgotpassword’s AndroidManifest.xml
you will see warning/errors because it can’t find its parent that is com.adrena.dfm.consumer.MainActivity
, and because of that, clicking home button in Forgot Password Activity
won’t do anything.
Lets fix this by creating flavor. Create a new file called flavors.gradle
and put it inside consumer
and partner directory.
Note: when adding flavor, all of your dynamic feature modules should also implement the same flavor.
Update all your consumer, partner and feature module’s build.gradle
Now navigate to Project, go to forgotpassword
, and create a new folder called consumer
and partner
, and create AndroidManifest.xml
in each directory
Now update AndroidManifest.xml
file in main
directory, remove all except manifest tag.
Update consumer’s AndroidManifest.xml
inside consumer
and partner
directory
As you can see above we’re using different dist:onDemand
for consumer
and partner
, we also updated parent activity to point to consumer/partner package name
Now try changing Build Variants to partnerDebug
and run the app, you should be able to go back to main activity now.
Don’t forget to remove .idea
directory (hidden), inside consumer app / partner app if you want to switch project, otherwise AndroidStudio might still pointing to wrong project.
And last, if you’re using different app name, you can use flavorImplementation
to point to different app name, e.g.:
consumerImplementation project(':app')
partnerImplementation project(':partner')
By using flavor, we’re able to reuse feature module in 2 apps. This tutorial is pretty simple because we only change AndroidManifest.xml file. In real life project, you can use this approach if you’re using dependency injection and need AppComponent (if you are using Dagger 2) in feature modules.
Source code available here: