Demystifying Android build variants

Slightly changing an Android app depending on various configurations is quite a common scenario. Some sample use-cases include having a separate build for non-production and production environments, white-labeling for different clients, or hiding sensitive data when open-sourcing.

But what's the easiest way to accomplish this, without changing values manually for each of the above cases?

Build types, product flavors, build variants

Some lingo first, because all these terms are used in Android Studio and Gradle without much explanation.  

  • Build type is the basic building block for having different configurations when building an Android app. The expectation is to build types to refer to the same app (e.g. debug and release build types: it's the same app but with some debug options enabled, different signing keys, etc).
  • Product flavors are another way to create different configurations, but the expectation here is that the result will refer to a different app (e.g. white-labeling for different clients).
  • The cartesian product of build types and product flavors (i.e. all the combinations between them) are the build variants.

Defaults

When you create a new project in Android Studio it comes with 2 build types: Debug and Release. You probably noticed them while exporting a signed APK/AAB; you have to choose one.

Debug type does not appear in build.gradle (app) but it's implied and comes with the debuggable true option. You can add it to the Gradle file if you want to customize more things. Release type appears and includes options for minification and ProGuarding.

Things you can do

Build types and Product flavors are defined in the module Gradle files (build.gradle (app)). Most of the options can be applied to both of them.

Note that each Product flavor you define must belong to a named Flavor dimension (i.e. a group of Product flavors). If only one flavor dimension exists, all flavors will be assigned to that dimension.

  • Different application ids: Useful for having both the prod and debug versions of your app installed at the same time.
android {
    [...]
    buildTypes {
        [...]
        
        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }
}
  • Different names: Application ids are not visible in recent Android versions. But the version name is. Useful for distinguishing which version is installed.
android {
    [...]
    flavorDimensions "version"
    productFlavors {
        demo {
            dimension "version"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            versionNameSuffix "-full"
        }
    }
}
  • Different resources: Quickly define resources in case you just need a different value. Or you can use the "Different sources" approach for having different XML resource files.
android {
    [...]
    buildTypes {
        release {
            resValue 'string', 'app_label', '"Prod version"'
        }
        debug {
            resValue 'string', 'app_label', '"Staging version"'
        }
}
  • Different sources

The files you see by default in Project explorer are located in src/main/java/ and is the main source set.  You can have files that are only included in the Debug or Release build type by placing them in src/debug/java/ or src/release/java/ respectively. Be careful to include source files only in a single source set; i.e. if you want to have a different Config.kt for debug and release, be careful to place the 2 versions in src/debug/java/Config.kt and src/release/java/Config.kt, but not in src/main/java/Config.kt (you might have to create some of these paths).

The same concept applies to all Build variants. For the examples above there are 4 variants (demoDebug, demoRelease, fullDebug, fullRelease) and you can place files that will be included only in a specific variant in src/buildVariant/java/.

You can do the same for resources, by placing them in src/buildVariant/res/.

Switching build variants

You can switch the active Build variant (i.e. the one you are currently editing in  Android Studio) by clicking the "Build Variants" tab.

Advanced features

The above examples were just a small taste of what you can do. You can check out all the available options for BuildTypes and ProductFlavors (e.g. having different dependencies, signing settings, filtering variants) or check some ready-made recipes to be inspired.  

Happy customizing your builds!