Modularizing your flavored Android project

Photo by Susan Holt Simpson on Unsplash

Intro

There are many reasons why you should at least consider splitting your monolithic application into smaller modules. You might be seeking for build time improvements, Google Play Instant support (previously known as Instant Apps) or better separation of concerns. I’m not going to go into details about how to actually modularize your app as there’s already a ton of useful articles available (links at the bottom). This article focuses on how to deal with library modules which require flavors.

So what’s the issue?

OK, so let’s say that you already have your app nicely divided into separate modules. Build time’s great, developing new features in the app seems like a breeze and all is fine… But then, for some reason, you have to introduce flavors in the app to e.g. have a paid version and a separate free version.

So you introduce flavors in your main app (the one with com.android.application in build.gradle) but then realize that some XML resources and classes in your other library modules also need to be overridden for your flavors as well. So you copy-paste your flavor configuration everywhere you need it.

The situation above is something that’s actually showcased in Google’s Instant Apps samples:

In main application’s build.gradle file you have:

And then in all feature/Android library modules:

Now, to be honest this is not necessarily a bad solution if you only have two flavors (with no need for more of them) and just a couple of modules.

However, as you add more modules and flavors this can get painful as you have to modify a lot of files and boilerplate code grows and grows… In our project we currently have 18 flavors with some config shared between flavor groups (I’ve written a separate article about this). As you can guess, in our case this could get really annoying…

But hey! In a Gradle world everything is possible!

The solution

To solve this particular task we can use Groovy’s with() method (more about it here) combined with a Closure containing our flavor config.

First, we create a flavors.gradle file in project root directory:

In 1 we export a closure so that we can use it in our modules’ build.gradle files.

In 2 we define a custom myApplicationIdSuffix property. We cannot simply have applicationIdSuffix as it is not possible to use it in library modules (build would fail if you did).

In 3 we iterate over created flavors and set applicationIdSuffix if we detect that it’s an application module only.

4 is a way to check where this closure is being used. In Android Gradle Plugin 3.2.0-beta we can use the code as it is but in 3.1.x we should use the one that’s commented out (the way for checking for application module changed).

All that’s left is to use this closure in our modules’build.gradle files. E.g. in application module this would look like this:

Which is much simpler!

If you’re interested in a fully working example you can jump in to the links from the next section. I hope this simplifies your Gradle config as it already had for us😄

Show me some samples

Google Play Instant flavored sample with changes proposed in this article:

Diff with proposed changes only:

Modularization links

Summary

Long story short, my recommendation to you is that if you have 2–3 flavors and only a few modules, you can ignore this article. However, if you’re dealing with a larger number of either, you might find our solution quite useful and worth considering.