Home Tutorials Training Consulting Books Company Contact us


Get more...

This tutorial describes the usage of the Gradle build system for building Android applications. It includes the information how to configure build flavors.

1. Gradle for building Android applications

1.1. Using Gradle for Android apps

By default, Android projects are handled by the Gradle build system. If you create a new project in Android studio, the Gradle build scripts are automatically created. Android studio provides the Gradle runtime, hence no additional installation is required.

If you press the run button in Android Studio, it triggers the corresponding Gradle task and starts the application.

You can also run Gradle via the command line. To avoid unnecessary local installation, Gradle provides a wrapper script which allows you to run Gradle without any local installation.

You find the available versions of the Android Gradle plug-in under the following URL: https://jcenter.bintray.com/com/android/tools/build/gradle/

1.2. Conversion process from source code to Android application

The Java source files are converted to Java class files by the Java compiler. The Android SDK contains a tool called dx which converts Java class files into a .dex (Dalvik Executable) file. All class files of the application are placed in this .dex file. During this conversion process redundant information in the class files are optimized in the .dex file. For example, if the same String is found in different class files, the .dex file contains only one reference of this String.

These .dex files are therefore much smaller in size than the corresponding class files.

The .dex file and other resources, e.g., the images and XML files, are packed into an .apk (Android Package) file. The program aapt (Android Asset Packaging Tool) performs this step.

The resulting .apk file contains all necessary data to run the Android application and can be deployed to an Android device via the adb tool.

As of Android 5.0 the Android RunTime (ART) is used as runtime for all Android applications. ART uses a combination of Ahead Of Time and _Just In Time _ compilation. During the installation of an application on an Android device, the application code is translated into machine code.

The dex2oat tool takes the .dex file created by the Android tool chain and compiles that into an Executable and Linkable Format (ELF file). This file contains the dex code, compiled native code and meta-data. Keeping the .dex code allows that existing tools still work.

1.3. Using Gradle on the command line

The Gradle build system is designed to support complex scenarios in creating Android applications:

  • Multi-distribution: the same application must be customized for several clients or companies

  • Multi-apk: supporting the creation of multiple apk for different device types while reusing parts of the code

You can start your Gradle build via the command line. Here is an overview of the important Android Gradle tasks:

Table 1. Android Gradle build targets
Command Description

./gradlew build

build project, runs both the assemble and check task

./gradlew clean build

build project complete from scratch

./gradlew clean build

build project complete from scratch

./gradlew test

Run the tests

./gradlew connectedAndroidTest

Run the instrumentation tests

To see all available tasks, use the gradlew wrapper command.

gradle build
# alternatively speedup second grandle build by holding it in memory
# gradle build --daemon

This command creates in the build folder the output of the Gradle build. By default, the Gradle build creates two .apk files in the build/outputs/apk folder.

To build and start your unit tests on the JVM use the following command.

gradle test

To build and start your instrumented tests on your Android device use the following command.

gradle connectedCheck

1.4. Removing unused resources and Java classes via resource shrinking

The Gradle build system for Android supports resource shrinking at build time. This automatically removes resources that are unused from the packaged application. In addition to that, this also removes unnecessary resources from libraries you are depending on. This can hugely reduce the size of your application.

To enable resource shrinking, update your build file similar to the following snippet.

android {
    ...

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

1.5. Defining dependencies and keeping the version external

A good practice is to define the version of your library dependencies outside the dependencies closure for better maintenance.

ext {
    // App dependencies
    junitVersion = '4.12'
    mockitoVersion = '1.10.19'
    powerMockito = '1.6.2'
    hamcrestVersion = '1.3'
}

dependencies {
    // Dependencies for local unit tests
    testCompile "junit:junit:$junitVersion"
    testCompile "org.mockito:mockito-all:$mockitoVersion"
    testCompile "org.hamcrest:hamcrest-all:$hamcrestVersion"
    testCompile "org.powermock:powermock-module-junit4:$powerMockito"
    testCompile "org.powermock:powermock-api-mockito:$ext.powerMockito"
}
If you put the ext closure into the root build file, you can access its properties for example with '$rootProject.ext.junitVersion'.

2. Building different flavors of your Android applications

2.1. Build types and build flavors

Android uses by default two build types: debug and release. For these build types you can create different flavors in you Gradle build.

The Gradle build system is also able to manage different flavors of an application. A product flavor defines a customized version of the application. This allows that some parts of the codebase or the resources can be different for variations of the app.

For instance, you can define different build variants for certain device categories, like phone or tablet. Another use case might be a paid or a free version of your app. Or you want to use different resources or classes during a test run.

2.2. Defining product flavors in your Gradle build file

You can use the productFlavors closure of you app/build.gradle file to define different variants of your product.

productFlavors {
    prod {
        applicationId = "com.vogella.android.gradlebuildflavors.prod"
        versionName = "1.0-paid"
    }

    mock {
        applicationId = "com.vogella.android.gradlebuildflavors.mock"
        versionName = "1.0-free"
    }
}

The whole build.gradle file might look like the following:

apply plugin: 'com.android.application'

android {
    // as before....

   flavorDimensions "version"
    productFlavors {
        prod {
            dimension "version"
            applicationId = "com.vogella.android.gradlebuildflavors.prod"
            versionName = "1.0-paid"
        }

        mock {
            dimension "version"
            applicationId = "com.vogella.android.gradlebuildflavors.mock"
            versionName = "1.0-free"
        }

    }
}

// as before

After defining these flavors you can select them in the Build Variants view in Android Studio.

build variants view

2.3. Providing different resources for the flavors

In order to define a different behavior for a certain flavor, you need to create suitable folders for the defined flavors under app/src/.

Flavor specific resources override the main resources. For example, if you provide a different application icon in a flavor the Android build system picks up the flavor specific one.

2.4. Providing different source sets for the product flavors

The directories in the src/ folder are called source sets. Every product flavor can define its own source set.

Code files are not replaced as resources, they are combined. For example, you cannot have a com.example.MainActivity activity in your app/main/java/ folder and a different implementation in another flavor. If you try this, you receive an error message about duplicate class definitions.

You can still provide different implementations by avoiding the creation of the class in your main source folder and instead create a class in each flavor.

3. Optional exercise: Using different product flavors for apps

In this exercise you create an Android application with two different project flavors, called prod and mock.

The mock version defines different resources than the prod version. In this first sample the strings.xml file of the main folder/project is overridden. Which variant is build is defined via the Build Variants view.

3.1. Creating a new Android application

Create a new Project with the Empty Activity template and the top level package com.vogella.android.gradlebuildflavors.

Define two additional product flavors in the app/build.gradle file called "prod" and "mock".

apply plugin: 'com.android.application'

android {
    // as before....

   flavorDimensions "version"
    productFlavors {
        prod {
            dimension "version"
            applicationId = "com.vogella.android.gradlebuildflavors.prod"
            versionName = "1.0-paid"
        }

        mock {
            dimension "version"
            applicationId = "com.vogella.android.gradlebuildflavors.mock"
            versionName = "1.0-free"
        }

    }
}

// as before

Create the desired folder structure for prod and mock flavors.

Resource directory for the paid flavor

Copy the strings.xml from the main folder to the appropriate folder of the flavor.

Change the hello_world string of the strings.xml to Mock World! and Prod World! accordingly.

<resources>
    <string name="app_name">Flavor</string>
    <string name="hello_world">Mock World! </string>
</resources>
<resources>
    <string name="app_name">Flavor</string>
    <string name="hello_world">Prod World! </string>
</resources>
flavour folder structure

3.2. Validate

Select mockDebug as Build Variant in the Build Variants view of Android Studio and run your app.

flavorselectioninbuildview

If you start you application you should see the string from the mock flavor. Select now you prod flavor and start it, you should see the other string.

3.3. Build via Gradle command line

Use the ./gradlew build command to build all your product flavors.

4. Providing different classes for testing and production

4.1. Prepare your application for testing with Gradle flavors

Create a class ShareIntentBuilder which starts an activity via the “share intent” via a static method with the following code.

import android.content.Context;
import android.content.Intent;

public class ShareIntentBuilder {

    public static void startSendActivity(Context context, String title, String body) {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.putExtra(Intent.EXTRA_TITLE, title);
        intent.putExtra(Intent.EXTRA_TEXT, body);
        intent.setType("text/plain");
        Intent chooserIntent = Intent.createChooser(intent, context.getResources().getText(R.string.share));
        chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(chooserIntent);
    }

4.2. Implement different MainActivity versions for your flavors

Allow the activity which triggers this intent to be replaced in your “mock” flavor. If in a flavor “mock” is selected, start a second activity in your application which displays the send data. If the flavor "prod" is selected send the shared intent.

Classes cannot be overriden, you need to create the classes in their specific flavors and not in the main flavor.

5. Customize Gradle build

5.1. Rename the output apk

apply plugin: 'com.android.application'

android {
    // more
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
       applicationVariants.all { variant ->
           variant.outputs.each { output ->
               def file = output.outputFile
               def filename = file.name.replace("app", "lars")
               output.outputFile = new File(file.parent, filename)

           }
       }
// more
}

5.2. Specify a different keystore for your debug build

You can define a keystore in your build.gradle file. See http://tools.android.com/tech-docs/new-build-system/user-guide for details.

For example, you can redefine the keystore for the debug variant:

android {
    signingConfigs {
        debug {
            storeFile file("your.keystore")
        }
    }
}

6. Migrating an Android project created with Eclipse to Gradle

6.1. Importing an Eclipse based Android project into Android Studio

Android projects come in two different configurations. Fhe first set of projects uses the legacy project structure used by the Eclipse ADT tooling which was used until 2013. The second set of project uses the new Gradle build structure. Gradle can be configured to support both formats, the Eclipse project structure and the Gradle project structure.

Once you added a valid Gradle file to your Eclipse based Android project you can import it into Android Studio, via File  Import Project and by selecting the project folder with the Gradle build file.

6.2. Adding a Gradle file for your Eclipse based Android project

To enable a Gradle build for your Eclipse based Android project addthe following build.gradle to the root of your project.

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0-beta3'
    }
}
apply plugin: 'com.android.application'


android {
     lintOptions {
          abortOnError false
      }

    compileSdkVersion 22
    buildToolsVersion "21.1.2"

        defaultConfig {
            targetSdkVersion 22
        }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')
    }
}

7. Android Gradle build links