Android Flavors

Motivation

Sometimes you have to deliver application for multiple environments, demo/full features and other configurations. Configuring this manually before build is not a good idea and here appears problem with Continuous Delivery and Continuous Deployment.

Let’s imagine that we have an application which works with some API endpoint and we should also delivered in Demo/Full mode.

Intro

The Android build system compiles app resources and source code and packages them into APKs that you can test, deploy, sign, and distribute. Android Studio uses Gradle as a build system.

Gradle is an open source build automation system that builds upon the concepts of Apache Ant and Apache Maven and introduces a Groovy-based domain-specific language (DSL) instead of the XML form used by Apache Maven of declaring the project configuration. Gradle uses a directed acyclic graph (“DAG”) to determine the order in which tasks can be run.

Gradle Project Structure

Although we won’t go deep into this topic, we will need some basic knowledge about project structure.

Project structure
  1. MyProject — Project
  2. app — Android application module
  3. androidTest — build type source set. Resources declared here are available only in Android Test Mode
  4. debug — build type source set. Resources declared here are available only in Debug mode
  5. main — Default source set. Every flavor/build type will contain everything from this set.

Flavor

Build variants are the result of Gradle using a specific set of rules to combine settings, code, and resources configured in your build types and product flavors. Although you do not configure build variants directly, you do configure the build types and product flavors that form them.

Although you can find some documentation of this in references, I will make a brief information of flavors definition and configuration.

build.gradle

Gradle build configuration file. You can see such one in project and inside modules. Also, here you can also find settings.gradle which defines what modules to include. In my case, MyProject has content :

include ‘:app’

This means that our project includes app module.

Defining flavors

Let’s create a sample application which will be compound of 2 versions, Full and Demo.

Add following lines of code in builld.gradle of App Module. These Flavors are added in default dimension(will cover this topic later).

android {
...
productFlavors {
demo {
applicationId "com.example.myapp.demo"
versionName "1.0-demo"
}
full {
applicationId "com.example.myapp.full"
versionName "1.0-full"
}
}
...
}

After adding this, in Android Studio Gradle Tasks you can find under :app section following tasks.

Android Gradle tasks

You can use terminal to check build tasks, to run

$ ./gradlew tasks // using gradle wrapper
$ gradle tasks // using gradle

and get following output under Build Tasks

Part of terminal output after running gradle tasks

Configuration is done. What’s next ?

One we had made have configured successfully, it’s time to define something specifics for this flavors. Into Android Studio project explorer, switch to “Project” mode and create following hierarchy

This means we had defined some specific strings for Demo mode and some for Full Mode.

Content of strings.xml in demo

<resources>
<string name="app_name">Demo App</string>
</resources>

Content of strings.xml in full

<resources>
<string name="app_name">Full App</string>
</resources>

Great, now you can switch to “Android” mode in Project Explorer. And now open “Build variants” panel in AS and you should have following variants:

Build variants in Android Studio

You see here combination of flavors with build types, that’s OK. Select one of them and try to run application in your device. For Demo variant you’ll have application with name Demo App and for Full you’ll have Full App.

Try to install both flavors in your phone, you’ll see that it’s not overriden, but we have 2 different apps called “Demo App” and “Full App”. That’s because we had specified applicationId property for both variants and they had different ID-s.

In same way you can define flavor specific resources,activities, manifest and … everything you want. Note that you Gradle will Flavor Specifics with defaults. For checking this, try to build an apk and analyze it with Android Studio.

Furthermore, you can specify resources for Demo variant and Debug Build Type. For this, you should create directory in

app/src/<variant><buildtype>

Flavor dimensions

In previous step we had defined 2 flavors “demo”, “full” but sometimes you need more flexibility. What if we will need more flexibility and to deliver following applications :

  • Demo App pointing to Live Environment
  • Full App pointing to Test Environment
  • or other configuration.

Here we can observe that we need another layer of flavors. Here comes in help flavor dimensions. You can think this in terms of “category”. Let’s update our app/gradle.config to be

android {
...
flavorDimensions "type", "environment"
    productFlavors {
Demo {
applicationId "com.example.myapp.demo"
versionName "1.0-demo"
dimension "type"
}
Full {
applicationId "com.example.myapp.full"
versionName "1.0-full"
dimension "type"
}
Test {
dimension "environment"
}
Live {
dimension "environment"
}
}
...
}

We defined added 2 dimensions “type” and “environment”.

Now, you’ll have following variants(also you can check gradle tasks, you’ll see same result). You can find here that Gradle mixed our dimensions with build types and we obtained following combinations

Variants with 2 dimensions

If this still doesn’t tell you nothing, then you can think that Gradle gives you following combinations

Specifying resources

Android plugin allows you to combine different flavors, but you still need to take care of naming conventions, here I denoted some of them. You can have more than 2 flavors, you can use in your own purpose.

/app/<flavor>
/app/<flavor><flavor>
/app/<flavor><flavor><buildtype>

Final word

We all know how great is Gradle and how much flexibility it gives to us. Additionally Android Plugin offers us Flavors for creating combinations of configurations. This can be useful when you have to deliver multiple types of application like in our example. This also gives you possibility automate everything for Continuous Delivery and Continuous Deployment.