Create different versions of your app using build variants

1. Introduction

Last Updated: 2022-04-14

Build variants are useful for creating different versions of your app. For example, you might want to build one version of your app that's free, with a limited set of content, and another paid version that includes more. You can also build different versions of your app that target different devices, based on API level or other device variations.

In this codelab, you modify the Dice Roller app you built in Unit 1: Kotlin Basics, so that you have two versions: "demo" and "full." The "full" version has an additional text box that displays the result of the die roll in words, so the user doesn't have to rely on just the die image for the roll result.

What you should already know

You should have:

  • Completed Android Basics learning pathways 1-4. To do the prerequisite codelabs, see the Unit 1: Kotlin Basics pathways.
  • A general understanding of the Android build system. To familiarize yourself with its main components, review the build overview.

What you'll learn

  • What build variants are.
  • What source sets are.
  • How to build different versions of your app using build variants and source sets.
  • How to give your app variants unique application IDs.

What you'll build

In this codelab, you start with the DiceRoller app that you built in the learning pathways of Unit 1: Kotlin Basics. The DiceRoller app has a picture of a die and a ROLL button below it. When the user clicks ROLL, the die is rolled and the picture changes depending on the roll outcome.

You create additional project files and add code to:

  • Create "demo" and "full" product flavors of your app.
  • Create "demo" and "full" source sets corresponding to the product flavors.
  • Add a box to the layout of the "full" version of your app.
  • Program the "full" version box to display the roll result when the user clicks ROLL.
  • Customize the app titles for the "demo" and "full" app versions.
  • Give the "demo" and "full" app versions unique application IDs.

Here's what the full version of your app will look like:

DiceRoller app with dynamic box.

What you'll need

2. Set up your environment

Get the code

If you don't have the completed DiceRoller app from Unit 1: Kotlin Basics readily available, download the app code from GitHub.

To download and open the app code in Android Studio, follow these steps:

  1. On the android-basics-kotlin-dice-roller-with-images-app-solution GitHub repo home page, click Code > Download ZIP.
  2. Once the ZIP file is downloaded, open the project in Android Studio and then click File > Open. You can start an Empty Activity or open a previous project if prompted. It doesn't really matter since we will be opening the downloaded project.
  3. Navigate to where your ZIP file is downloaded (probably your Downloads folder), select it, and then click Open.

Use Project view

When you work with build variants, you need to work with your project files in Project view to see all the directories for the different variants. To do this, open the Project pane in Android Studio, click the view type menu (it's set to Android view by default), and select Project view.

3. Understand build variants

Build variants are the result of different combinations of product flavors and build types. You can think of product flavors as more user-facing attributes, and build types as more developer-facing attributes. You don't actually configure the build variants directly; rather, you configure a set of product flavors and a set of build types, which in turn determine the build variants.

Specifically, the build variants represent every combination of product flavor and build type, and are named <product-flavor><build-type> accordingly. For example, if you had the build types debug and release and the product flavors demo and full, the resulting build variants are:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Let's configure our product flavors and build types for the DiceRoller app.

4. Configure product flavors

Product flavors are the more user-facing attributes of your app in the sense that they usually represent the app versions available to the user. To create the demo and full versions of our app, you have to add the two product flavors and assign them to a flavor dimension. To add the product flavors, open the app-level build.gradle file (app > build.gradle in Project view) and paste this code in the android {} block.

flavorDimensions "app_type"  
productFlavors {
   demo {
       dimension "app_type"
         }
   full {
       dimension "app_type"
   }
}

This code does these things:

  • Creates a flavor dimension called app_type.
  • Creates two product flavors, represented by the demo {} and full {} blocks.
  • Assigns the two product flavors to the app_type dimension (this is optional if there is only one flavor dimension).

This is the basic code needed to define product flavors. You'll use some additional options later in this codelab.

5. Configure build types

Build types are more developer-facing attributes of your app in the sense that they usually represent stages of development (for example, debug, beta, and release). Android Studio automatically configures two build types for you: debug and release. The debug build type is for debugging purposes, whereas the release build type is for distribution purposes.

You can create build types and modify their configurations by editing the buildTypes {} block in the app-level build.gradle file. The default buildTypes {} block looks like:

buildTypes {
   release {
       minifyEnabled false
       proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
   }
}

You can see the default configuration for the release build type (we won't cover the minifyEnabled or proguardFiles properties in this codelab). The debug build type doesn't appear in the build configuration file by default, but you can add a debug {} block to add or change certain settings. The default configuration works for our purposes, so you can leave it as is.

6. Use the Build Variants tool window

Now that you have two product flavors and two build types, let's see what build variants they create. To view your new build variants in Android Studio:

  1. In the toolbar, click Sync Project with Gradle Files 4e7fbf18152549d8.png. Whenever you make changes to build configuration files, Studio prompts you to sync your files so that it can save the new build configuration and check for build errors.
  2. Click Build > Select Build Variant (or View > Tool Windows > Build Variants) to display the Build Variants window.
  3. Click demoDebug in the Active Build Variant column to open a menu that contains all your build variants: demoDebug, demoRelease, fullDebug, and fullRelease. Use this menu to select different build variants to run and test.

Build Variants tool window with Active Build Variant menu displayed.

Run the demoDebug and fullDebug variants (the variants based on the release build type require more setup to run, so we won't deal with them in this codelab). The demoDebug and fullDebug variants are indistinguishable from a user perspective so far.

7. Create different features for different variants

Now that you have your demo and full app versions, let's add some features to the full version.

Add a premium feature using source sets

For the full product flavor, let's add a box that shows the result of the roll, so users don't have to rely on just the die image to know what they rolled. To add this premium feature, you use source sets. A source set is a directory that contains content for specific build types, product flavors, and build variants. The default main source set (app > src > main) contains content to share among all build variants. This default content can be overridden by content in other source sets.

The way you override what's in the main source set and determine which source sets you need to add differs depending on what you want to change.

To change a resource, such as a layout or image (something in main > res), follow these general steps:

  1. Create a source set, or directory, for the product flavor you want to change (we'll cover where and how to create this source set in the next section).
  2. Paste the resource file you want to change into this new source set and then update it. When the Android Gradle plugin (AGP) builds a build variant that depends on the new source set, it overrides the resource code in main with the resource code in the new source set.

To override a behavior in a Java or Kotlin class (something in main > java), follow these general steps:

  1. Create source sets, or directories, for the product flavor you want to change and all the other product flavors in the same flavor dimension.
  2. Paste the file you want to change into all the product flavor source sets, and then modify the copy (or copies) you want to change.
  3. Delete the original file from the main source set.

To add a dynamic box to the full version of your app, it's helpful to think of the premium feature as two main edits:

  • Customize the layout: Add a box by modifying the activity_main.xml file.
  • Customize the behavior of the app: Change what shows in the box based on the die roll by modifying the MainActivity.kt file.

In the next section, you learn where and how to create source sets.

Determine where to create new source sets

The Android Gradle plugin (AGP) shows you how to organize your files for each of your build types, product flavors, and build variants. To add a premium feature to the full product flavor that includes a change to the MainActivity.kt file, you create source sets for both the full and demo flavors. To find out where to create these source sets, follow these steps in Android Studio:

  1. Click Gradle in the IDE window.
  2. Navigate to MyApplication > Tasks > android and double-click sourceSets. If you don't see the Tasks folder, let Gradle build the task list during sync by clicking File > Settings > Experimental and unchecking Do not build Gradle task list during Gradle sync (Android Studio > Preferences > Experimental on macOS). After Gradle executes the task, the Run window should then display the output.
  3. If the display is not in text mode as shown here, click Toggle view 82487fd4e011f105.png in the Run window.

In the Run window, you should see this output:

------------------------------------------------------------
Project ':app'
------------------------------------------------------------

...

debug
-----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

...

full
----
Compile configuration: fullCompile
build.gradle name: android.sourceSets.full
Java sources: [app/src/full/java]
Kotlin sources: [app/src/full/kotlin, app/src/full/java]
Manifest file: app/src/full/AndroidManifest.xml
Android resources: [app/src/full/res]
Assets: [app/src/full/assets]
AIDL sources: [app/src/full/aidl]
RenderScript sources: [app/src/full/rs]
JNI sources: [app/src/full/jni]
JNI libraries: [app/src/full/jniLibs]
Java-style resources: [app/src/full/resources]

The list of file paths under "debug" describes where AGP looks for app code and resources for the "debug" product flavor by default. Similarly, the list of file paths under "full" describes where AGP looks for content for the "full" product flavor by default.

Take note of the "Java sources" (for files such as Java and Kotlin classes) and "Android resources" (for files such as layouts and images) file paths. Now that you know where to create new source sets, create them and add code for the premium feature.

Customize the layout of the full version

To add a box to the full version of your app, follow these steps:

First, create the full source set and Java resources directory:

  1. Open the Project pane and select the Project view.
  2. Navigate to the DiceRoller/app/src/ directory.
  3. Right-click the src directory and then select New > Directory.
  4. From the menu under Gradle Source Sets, select full/resources and then Press Enter.

Second, paste the activity_main.xml file from the main source set to the full source set.

  1. Navigate to DiceRoller/app/src/main/res/.
  2. Right-click on the activity_main.xml file and then click Copy.
  3. Navigate to DiceRoller/app/src/full/res/.
  4. Right-click on the res directory and then click Paste.

Third, to add a box to the full flavor of your app, add this code to the activity_main.xml file in the full source set:

<TextView
    android:id="@+id/resultTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="16sp"
    android:text="Result"
    app:layout_constraintBottom_toTopOf="@+id/button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/imageView" />

Run the fullDebug and demoDebug build variants of your app (select the build variant you want to run using the Build Variants tool window and then click Run). You should see the new box for the fullDebug variant only. The box doesn't do anything yet though, so let's program it to change depending on the die roll.

DiceRoller app with box that says

Customize the behavior of the full version

For the full version of the app, the box should display the result of the die roll, in words. To program this behavior, follow these steps:

First, create the full source set Java sources directory.

  1. With the Project pane in Project view, navigate to the DiceRoller/app/src/full directory.
  2. Right-click the full directory and then select New > Directory.
  3. From the Gradle Source Sets menu, select java and then press Enter.

Second, create the debug source set and Java sources directory.

  1. With the Project pane in Project view, navigate to the DiceRoller/app/src directory.
  2. Right-click the src directory and then select New > Directory.
  3. From the Gradle Source Sets menu, select debug/java and then press Enter.

Third, navigate to the DiceRoller/app/src/main/java directory.

  1. Paste the MainActivity.kt file into both the full/java and debug/java directories.
  2. Delete the java directory, including the MainActivity.kt file, from the main source set. (Right-click on the java directory and click Delete.)
  3. In the full source set MainActivity.kt file, add this code to the rollDice() method:
// Update the result text view
val resultTextView: TextView = findViewById(R.id.resultTextView)
resultTextView.text = when (diceRoll) {
    1 -> "One"
    2 -> "Two"
    3 -> "Three"
    4 -> "Four"
    5 -> "Five"
    else -> "Six"
}

Run the fullDebug and demoDebug build variants again. The box in the full version should include the die roll result.

DiceRoller app with dynamic box.

Change the app titles

For clarity, let's specify which version of the app you are using in the app title. This type of change can actually be done without source sets. The app title is defined by the label property in the AndroidManifest.xml file (app > manifests > AndroidManifest.xml) . To get the label value to change depending on the variant being run, open the AndroidManifest.xml file and change the label line to be:

android:label="${appLabel}"

This assigns a variable, appLabel, as the app title.

Next, to set the value of appLabel or change the title for the demo version of the app, add the manifestPlaceholders line to the demo {} block you created earlier:

demo {
   dimension "version"
   manifestPlaceholders = [appLabel: "Dice Roller - Demo"]
   applicationIdSuffix ".demo"
}

Similarly, to change the title of the full flavor of the app, add another manifestPlaceholders line to the full {} block:

full {
   dimension "version"
   manifestPlaceholders = [appLabel: "Dice Roller - Full"]
   applicationIdSuffix ".full"
}

Now run the demoDebug and fullDebug variants again. You should see the different titles now for the different build variants.

Completed DiceRoller app with dynamic box.

8. Give unique application IDs to your build variants

When you build an APK or AAB for your app, the build tools tag the app with the application ID defined in the defaultConfig {} block from the app-level build.gradle file (as shown below). However, if you want to create different versions of your app to appear as separate listings on Google Play Store, such as a "demo" or "full" version, you need to give each version a different application ID. For each flavor inside the productFlavors {} block, you can redefine the applicationId property, or you can instead append a segment to the default application ID using applicationIdSuffix, as shown here:

defaultConfig {
   applicationId "com.example.diceroller"
   ...
}

flavorDimensions "version"
productFlavors {
   demo {
 dimension "version"
       manifestPlaceholders = [appLabel: "Dice Roller - Demo"]
         applicationIdSuffix ".demo"
   }
   full {
       dimension "version"
       manifestPlaceholders = [appLabel: "Dice Roller - Full"]
         applicationIdSuffix ".full"
   }
}

9. Congratulations

Congratulations, you've successfully built two versions of the DiceRoller app using build variants!

You configured product flavors and build types to create build variants. You added a premium feature to the "full" app version using source sets. You learned how to give each build variant a unique application ID so that they are considered separate apps in the Play Store.

You now know the foundational steps required to create multiple versions of an app to better serve different user groups.

What's next?

Reference docs