Android Build Types and Product Flavors

Filed Under: Android

In this tutorial, we’ll be discussing Android Build Types and Product Flavors. We’ll see how they make our Android Development easier and faster especially when we’re creating applications with minimal differences. These differences can be as small as changes in themes and app icons or can be for different stages of the product such as dev, beta, production etc.

Create a new project in your Android Studio and choose the Basic activity. In the next section, we’ll look at build types.

Android Build Types

Once the new project is created, by default it consists of two build types/variants – debug, release.

Debug is the build type that is used when we run the application from the IDE directly onto a device.

A release is the build type that requires you to sign the APK. The release builds are meant to be uploaded to the play store. In the release build type, we obfuscate the code using ProGuard to prevent reverse engineering.

Following image shows the default build types.
android build types variants

In the build.gradle by default, only the release build type block is written:


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

We can add properties on the other build types too.
Before we do that let’s add some signingConfigs to the android block.


signingConfigs {
        release {
            storeFile file("release-key.keystore")
            storePassword 'password'
            keyAlias 'alias'
            keyPassword 'journaldev'
        }
    }
Make sure you’ve created a signed key file with the release-key name and the above password from Build | Generate Signed APK for the above code to work.

Let’s add new build types and more properties to the buildConfig.


buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug{
            applicationIdSuffix ".debug"
            versionNameSuffix "-debug"
        }

        beta{
            signingConfig signingConfigs.release
            applicationIdSuffix ".beta"
            versionNameSuffix "-beta"
        }
    }

applicationId suffix appends the string to the applicationId of the application.

versionName does the same on the version name present in the defaultConfig.

Now we have 3 build variants:

android build type variants

After running the debug build on our device, we went to Settings | Applications | Our App Name. Following is the screenshot of the app info:

android build types debug screenshot app info

The version number at the bottom has changed. This is useful to differentiate between different builds.

BuildConfig

The BuildConfig.java class is auto-generated when different buildFlavors are created.
We can set Build Config Fields in our build.gradle.


buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug{
            applicationIdSuffix ".debug"
            versionNameSuffix "-debug"
            buildConfigField "String", "TYPE", '"I AM A DEBUG NINJA"'
        }

        beta{
            signingConfig signingConfigs.release
            applicationIdSuffix ".beta"
            versionNameSuffix "-beta"
            buildConfigField "String", "TYPE", '"I AM A BETA NINJA"'
        }
    }
android build config auto generated

BuildConfig for the beta build

The BuildConfig.java class and its fields can be accessed by our Activities directly.

We can also add resources fields using in the buildConfigs. Add the following in your beta buildConfig and it’ll be auto-created in the resources | strings.xml folder.
resValue "string", "my_name", "Anupam Beta"

Android Product Flavors

Android Product Flavors are used to create different app versions. App versions can be free or paid. They can have different themes and texts. They can use different environments or APIs.

Let’s assign two product flavors free and paid in our application.


productFlavors{

        free{
            applicationId "com.journaldev.androidproductflavors.free"
        }

        paid{
            applicationId "com.journaldev.androidproductflavors.paid"
        }
    }

For the above code in the build.gradle to successfully create the flavors we need to set flavor dimensions.

Flavor Dimensions is a way to group flavors by a name. For now, we’re using just a single group.

Add the following line in your defaultConfig block:


flavorDimensions "default"

Now syncing the gradle would give you the following product flavors:

android product flavors variants

Android Build Variants combine build types and product flavors. They create a matrix of all combinations.

Now in our project, the main folder consists of the common logic across all app versions. To write flavor specific code, create the folder with the same name as the flavor.

  • The java classes with the same name in the flavor folders won’t override the main folder.
  • The res folder in the main should only have those directories that are common to all flavors.

Now let’s create the free and paid folders in our project and create separate res folders for each.

Project Structure

android-product-flavors-project-structure

So in the free and paid folders, we’ve created res folders in which the app icons and strings.xml are different for each flavor.

Our final build.gradle file looks like this:
android product flavors build gradle

content_main.xml class is given below:


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/textViewLabel"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

In the textView, we set the string from the strings.xml. The strings.xml resources for each of the flavors contains the same key:


<string name="textViewLabel">Hello free</string> - For free
<string name="textViewLabel">Hello Paid</string> - For paid.

MainActivity.java:


package com.journaldev.androidproductflavors;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Flavour: "+BuildConfig.FLAVOR + " My type: "+BuildConfig.TYPE , Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

We’ve run two flavors:
freeDebug and paidDebug on our device.

Here’s the look of them:

android product flavours app output

WOW! The left app is the free one and right one is the paid one.

Let’s launch each of them. Following are the screenshots from them, side by side.

android product flavors

WOW! Different colored FloatingActionButton and application names.

Try clicking on the FAB and you’ll see a flavor specific SnackBar text.

Manifest Placeholders

You can set the app name for each product flavor directly in the AndroidManifest.xml file without the need to create separate strings.xml files, using Manifest Placeholders in your build.gradle.

Following is the way they are defined:
android product flavors manifest placeholders

Now we can use the appLabel key in the AndroidManifest.xml file as:
android product flavors manifest

This brings an end to this tutorial. You can download the project from the link below:

Comments

  1. Rabiun Islam says:

    very well explained within few words. Excellent

  2. Siddharth Karnik says:

    Great tutorial, very informative.
    Though I was also expecting that you show about the project structure for implementing flavour specific java classes or activities .

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages