ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Advanced Android Flavors Part 3— Flavorful Libraries

This is the third and final article in my ‘Advanced Android Flavors’ series.
The first one is here.
The second one is here.
The fourth one is here.
The fifth one is here.

Let’s Add A Library

Now that our project’s configuration is nice and complex, let’s add yet another layer of complexity, shall we?

We now want to tackle flavors inside connected library projects. For that purpose, let’s say the app we’ve been working on, from now on named app1, uses a library to perform common actions. We could also have app2 and app3 using the same library and we can name it Common.

And guess what? It has flavors too. The Common library’s flavor list is:

android {

//...

flavorDimensions "app", "server"
productFlavors {
app1 {
dimension "app"
}
app2 {
dimension "app"
}
app3 {
dimension "app"
}
dev {
dimension "server"
}
staging {
dimension "server"
}
production {
dimension "server"
}
}

Let’s break it down:

  • The app’ dimension: our shared library has a bit of custom code that’s tailored for each application.
  • The ‘server’ dimension: the same thing we had in our app. Why? Because our networking layer has moved into the Common library.

We’ve connected our Common library project into our app project. As a result, we now have a new addition to our Build Variants menu. It appears as a new module named Common. We can choose its flavor exactly the same way we chose the app flavor.

We could switch both the app and the library config from the Build Variant menu by hand and call it a day. But we don’t want to have to go through two dropdown menus every time we want to change the configuration.
Let’s automate it!

Gradle Updates

Remember our six configurations from the previous post? We’ll choose the correct library config for each one. For that, we have make two additions to our Gradle file.

First, we need to declare what configurations we’ll be using. These are actually Gradle compile jobs. In our build.gradle file, between the android and the dependencies sections, we add a new section:

configurations {
client1DevCompile
client1StagingCompile
client1ProductionCompile
client2DevCompile
client2StagingCompile
client2ProductionCompile
}

Above, we declared Gradle compilation jobs for each one of our app configs. We now have access to them in our dependencies section. Next, we can marry each of these jobs with the correct Common library configuration:

dependencies {
client1DevCompile project(
path: ':common',
configuration: 'app1DevRelease'
)
client1StagingCompile project(
path: ':common',
configuration: 'app1StagingRelease'
)
client1ProductionCompile project(
path: ':common',
configuration: 'app1ProductionRelease'
)
client2DevCompile project(
path: ':common',
configuration: 'app1DevRelease'
)
client2StagingCompile project(
path: ':common',
configuration: 'app1StagingRelease'
)
client2ProductionCompile project(
path: ':common',
configuration: 'app1ProductionRelease'
)
// other dependencies}

We declared the exact library configuration for each one of our app configurations. Let’s break it down once again:

  • Every config starts with app1, since that’s the app we’re working on (as opposed to app2 or app3).
  • Next is the server dimension. Here, we’re matching the app and library server config.
  • Finally, we have release. This means we choose to always compile our library in release mode. This is a personal preference, and you can replace it with debug if you wish.

Now, try to switch the app configuration in the Build Variant menu. The library configuration switches in tandem. No more manual switching!

A Few Words About Build Types

I have ignored build types so far, so I’ll go into a little more detail about it. It won’t be as comprehensive as my flavor coverage but it’s good to know how it’s connected to flavors.

The build types are a layer above our flavors. Each flavor will have a version for debug, release, and any other build type you might have.

You could, for instance, split the client1DevCompile config into client1DevDebugCompile and client1DevReleaseCompile. Then, map each one of them to the appropriate debug and release library config. I prefer to have my library compiled in release mode.

The End?

As we saw, Android Flavors are quite a powerful and flexible way to create variations of the same app. You can do anything from a simple demo version to an army of white label applications. It all depends on your taste.
(See what I did there?)
This used to conclude my original three part series. But since the Gradle version update, I’ve added more entries to this series.

Check out: What’s new in Gradle v3.0.0

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Responses (10)

Write a response

Instead of library modules with flavors, should not the libraries be agnostic and instead, our responsability to use them with the appropriate params from app modules?

--

Hi, great reading! I do have a question:
How would an app without any flavors deal with a local library that does have flavors?

--

Will your solution help if I need to exclude some code from sub-project? I have app project (3 configs: 1 per platform) and library project. For app config #2, part of library code should be excluded. Can I use “path” to include the code only from specific package in library? Or it is better to use “exclude”?

--