Gradle Tips and Tricks for Android

Learn some cool tricks for working with the Gradle build system, such as autocompletion, using build-time variables, and reducing build time!

Version

  • Kotlin 1.2, Android 5.1, Android Studio 3

Gone are the days when you will be having a cup of coffee and browsing the Internet just after you have started the build for your Android project because it takes ages to complete — or having to fiddle with customizing your build system. The current state of build automation tools handles several aspects of building a project more efficiently than in the past. From taking care of complex tasks to making sure all tests have passed while you spend time writing new features and crushing bugs in your codebase.

Gradle is a modern build automation tool that acts as the Swiss army knife for building your Android and other projects. For Android development, you access and utilize Gradle via the Android Studio Gradle plugin.

In this tutorial, you will learn some serious Gradle-fu tricks by building a simple Gradle Playground Android app. In the process, you’ll learn:

  • Autocompleting and IDE friendly dependency management using the buildSrc folder.
  • APK splits for multi-platform APK generation when using the NDK.
  • Passing build-time variables to your Java, Kotlin and Native code.
  • Handling dependency version conflicts.
  • Reducing build time.
Note: You typically will use Groovy for writing your Gradle build files. Groovy is widely used and is also the default in Android Studio out of the box.

This Gradle tips-and-tricks tutorial assumes that you know the basics of Android development and working with Gradle. If you’re new to Android Development, it’s highly recommended that you work through Beginning Android Development and Kotlin for Android to learn about the basic tools and concepts.

If you are new to the Gradle build system, also check out Gradle Tutorial for Android: Getting Started.

Other prerequisites include knowledge of using the Bash shell, the Terminal and Android Studio 3.1 or later.

Understanding Gradle

Gradle is an open-source build automation system. It uses a Groovy-based domain-specific language (DSL) instead of the common XML for declaring project configurations in other build systems in the Java ecosystem. Recently, a Kotlin-based DSL for Gradle has also become an option.

Gradle supports incremental builds, makes intelligent decisions to skip building up-to-date dependencies and makes use of caching at multiple stages. All of this makes sure build times are small, in most cases. Apart from these features, Gradle is highly customizable. It will allow you to pass flags to your code, manage dependencies, and it supports pre- and post-processing your build outputs.

Getting Started

To get started with Gradle for this tutorial, begin by downloading the materials at the top or bottom of the page using the Download materials button.

If you already have Android Studio 3.1.3 or later open, click File ▸ Import Project and select the top-level project folder you just downloaded. If not, start up Android Studio and select Open an existing Android Studio project from the Welcome screen, again choosing the top-level project folder for the starter project you just downloaded.

If you’ve never worked with the Android NDK before, you may be prompted to install the NDK and CMake when first opening the starter project in Android Studio. If you are, just click the links provided in associated the build panels:

Install NDK

Install CMake

Take some time to familiarize yourself with the project before you move on. Notice that the starter app already has the Kotlin language plugin setup and there is a MainActivity that contains an ImageView, a TextView and a button.

Build and run the app. You should see the following:

Starter app

Note: It’s worth mentioning, because you have set up Native code compilation in your project, the builds are a little bit slower because now there is an added step of compiling the Native code.

Native code support can be added to new projects by checking the Include C++ Support option when you create a new Android app via the Create Android Project wizard.

C++ Support in Project

As of now, the Gradle Playground app only shows some basic information. You will work mostly in the app module’s build.gradle file to implement new Gradle-based features using various tips and tricks as you move forward.

Time to jump right into the playground!

Using Dependency Management

With the pace at which the Android ecosystem is moving, and with the introduction of Instant Apps and their need for multi-module projects, every Android developer spends considerable time managing multiple dependencies and their versions across various modules in the project. The common approach is to do it manually, which is very error-prone in addition to being cumbersome to maintain in the long run.

Wouldn’t it be better if you had a central location to update the dependency version and all modules inherited from it?

Gradle comes with built-in support for dependency management. There are various approaches to doing this; the most common one (and officially recommended by Google) is to use ext variables. This is a good approach but it has its drawbacks, too. For instance, you cannot use IDE autocomplete functionality to add your dependencies in the build.gradle file of individual modules. This means you are still copy-pasting between modules. Also, code navigation to the declaration of the dependency version is not supported.

It turns out that, with some Kotlin and Gradle magic, you can make this experience much better.

Note: From the Gradle Documentation:

When you run Gradle, it checks for the existence of a directory called buildSrc. Gradle then automatically compiles and tests this code and puts it in the classpath of your build script. You don’t need to provide any further instruction.

This sounds interesting.

You will set up dependency management using Kotlin and buildSrc folder.

First, set up a folder at your root level called buildSrc. To do that:

  • Switch to Project view and right-click on your project root folder. Select New ▸ Directory:
    Click on new directory
  • Name your new directory buildSrc:
    Create buildSrc directory
  • Once created, you will have a project directory structure like below:
    buildSrc directory in project view
  • Right-click on the created buildSrc folder and create a New ▸ File named build.gradle.kts:
    Create build.gradle.kts file
  • Edit your build.gradle.kts file and add the kotlin-dsl plugin as shown below:
    import org.gradle.kotlin.dsl.`kotlin-dsl`
    
    plugins {
        `kotlin-dsl`
    }
    

    Once done, click Reload dependencies:

    Reload dependencies

  • Next, right-click on the buildSrc folder and select New ▸ Directory. Enter src/main/java to create multiple nested folders:

    New Package Dialog

  • Once created, navigate to the buildSrc/src/main/java folder. Right-click on the java folder and select New ▸ File. Create a file named Dependencies.kt:

    Dependencies.kt file

  • Now, edit the Dependencies.kt file and add Kotlin code to declare the dependencies and versions as constant immutable variables. To have a bit of separation, versions are declared inside a singleton named Versions and the dependency inclusion rule is declared inside a singleton named Deps:
    // 1
    object Versions {
      // 2
      const val kotlin = "1.2.50"
    }
    
    // 3
    object Deps {
      // 4
      const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}"
    }
    

    Here you:

    1. Define an object (singleton in Kotlin) named Versions.
    2. Define a constant named kotlin and assign it a value that is the version of Kotlin language plugin you are using.
    3. Define another object named Deps.
    4. Define a constant named kotlinStdLib and assign it a value that is the string for the Kotlin standard library dependency. Note how the kotlinStdLib value can reference values from the Versions singleton.

    You can declare more variables here such as your minSdkVersion, targetSdkVersion, versionCode and even Android Gradle plugin version. Update the Dependencies.kt file to include all the version numbers and dependencies you need:

    object Versions {
    
      // Build Config
      const val minSDK = 14
      const val compileSDK = 27
      const val targetSDK = 27
    
      // App version
      const val appVersionCode = 1
      const val appVersionName = "1.0.0"
    
      // Plugins
      const val androidGradlePlugin = "3.1.3"
    
      // Kotlin
      const val kotlin = "1.2.50"
    
      // Support Lib
      // const val support = "27.1.1"
      // Comment above and uncomment below to cause conflict in dependency
      const val support = "26.0.1"
      const val constraintLayout = "1.1.0"
    
      // Testing
      const val junit = "4.12"
      const val espresso = "3.0.2"
      const val testRunner = "1.0.2"
    
    }
    
    object Deps {
    
      // Plugins
      const val androidGradlePlugin = "com.android.tools.build:gradle:${Versions.androidGradlePlugin}"
    
      // Kotlin
      const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}"
      const val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}"
    
      // Support Library
      const val appCompat = "com.android.support:appcompat-v7:${Versions.support}"
      const val supportAnnotations = "com.android.support:support-annotations:${Versions.support}"
      const val constraintLayout = "com.android.support.constraint:constraint-layout:${Versions.constraintLayout}"
    
      // Testing
      const val junit = "junit:junit:${Versions.junit}"
      const val espressoCore = "com.android.support.test.espresso:espresso-core:${Versions.espresso}"
      const val testRunner = "com.android.support.test:runner:${Versions.testRunner}"
    }
    

    This is all you need to set up the whole dependency management system. You’ll use this now in your app module.

    Open the app module’s build.gradle file, go to the dependencies block and replace the Kotlin stdlib dependency by typing Deps.ko. Wait for autocomplete to suggest kotlinStdLib to you:

    // Replace this
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    
    // With 
    implementation Deps.kotlinStdLib
    

    Dependency Management

    Update the full dependencies block to be:

    dependencies {
      implementation fileTree(dir: 'libs', include: ['*.jar'])
    
      // Kotlin
      implementation Deps.kotlinStdLib
    
      // Support Libraries
      implementation Deps.appCompat
      implementation Deps.constraintLayout
    
      // Testing
      testImplementation Deps.junit
      androidTestImplementation Deps.testRunner
      androidTestImplementation Deps.espressoCore
    }
    

    I’ll leave it for you as a challenge to use the other version numbers from Dependencies.kt. For example, the compileSdkVersion becomes:

    compileSdkVersion Versions.compileSDK
    

    Check out the final project if you run into any trouble.

    Once you’re done, you can sync your project with Gradle and run the app. You will see the app runs successfully as before:

    Starter app

    You now have autocompletion and code navigation (in Android Studio) for jumping to definitions enabled for your dependencies from any module. You only need to make the changes once in the Dependencies.kt file and it works across all modules.

    Handling Dependency Conflicts

    It is pretty common to encounter conflicts when you move to a cross-module dependency management system like the one explained above. For example, one of your modules might transitively depend on a different version of a dependency but another module depends on another version of the same dependency. Having different versions of the same dependency can cause inconsistencies in the project.

    You can use Gradle to define strategies around dependency resolution in the event of conflicts. For example, for forcing certain dependency versions, using substitutions, and conflict resolutions.

    First, you set up the project to show version conflicts. Add the following code block after the dependencies block in the app module build.gradle file, replacing // TODO: Add Dependency Conflict Management block here:

    // 1
    configurations.all {
      // 2
      resolutionStrategy {
        // 3
        failOnVersionConflict()
      }
    }
    

    Here you:

    1. Declare the task that will monitor all configurations.
    2. Define the resolution strategy in case of conflicts.
    3. Fail eagerly on version conflict (includes transitive dependencies), e.g., multiple different versions of the same dependency (group and name are equal).

    Add a new dependency to your app module build.gradle file:

    implementation "com.android.support:support-annotations:26.0.1"
    

    Next, in Dependencies.kt change the version of support:appcompat-v7 specified in the support constant to 27.1.1 from 26.0.1. Sync the project with your Gradle files, and you will see that the project doesn’t compile and instead shows the below error:

    Dependency Conflict

    Why did this happen? Because one of the dependencies depends on a different version of support:appcompat-v7 library.

    To fix this conflict, update the resolutionStrategy block as below:

    configurations.all {
      resolutionStrategy {
        failOnVersionConflict()
    
        // 1
        preferProjectModules()
    
        // 2
        force 'com.android.support:support-annotations:26.0.1'
    
        // 3
        forcedModules = ['com.android.support:support-annotations:26.0.1']
    
        // 4
        dependencySubstitution {
          substitute module('com.android.support:support-annotations:27.1.1') with module('com.android.support:support-annotations:26.0.1')
        }
      }
    }
    

    Here you:

    1. Prefer modules that are part of this build (multi-project or composite build) over external modules.
    2. Force certain versions of dependencies (including transitive), append new forced modules.
    3. Force certain versions of dependencies (including transitive), replace existing forced modules with new ones.
    4. Add dependency substitution rules, e.g., replace one dependency with another.

      Note: This property is incubating and may change in a future version of Gradle.

    What you have done is told Gradle to force the version the 26.0.1 for the support:support-annotations in all configurations.

    Once these changes are added and Gradle is synced, there will be no more conflict errors! Great work at fixing them!

    Run the app. You will see the app runs successfully as before:

    Starter app

    Implementing APK Splits for Native Code by Platform

    For those of you who work with native C and C++ code in Android projects, you probably have been bundling the compiled native code and the native shared library for all platform types (armeabi-v7a, arm64-v8a, x86, x86_64) along with your generated .apk file, leading to huge .apk sizes.

    What if I told you there is a simple trick to make your APK file smaller by only bundling compiled native code for only one platform type and then generating a different APK for each? Gradle makes it possible once again, via the splits block

    Note: From the official documentation:

    The splits block is where you can configure different APK builds that each contains only code and resources for a supported screen density or ABI.

    You can even split your APK based on the density of pixels and, with a little bit of Groovy magic, you can make sure each of these .apk files has a version that is dynamically generated.

    To use splits, simply add the splits code block and two other items within the android block in your app module’s build.gradle file, replacing // TODO: Add .apk splits block here:

    android {
      ...
      // Add this block
      splits {
    
        // 1
        abi {
    
          // 2
          enable true
    
          // 3
          reset()
    
          // 4
          include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
    
          // 5
          universalApk true
        }
    
        // 6
        density {
    
          // 7
          enable false
    
          // 8
          exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
        }
      }
    
      // 9
      project.ext.versionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
    
      // 10
      android.applicationVariants.all { variant ->
        // assign different version code for each output
        variant.outputs.each { output ->
          output.versionCodeOverride =
              project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) *
                  100000 +
                  android.defaultConfig.versionCode
        }
      }
    }
    

    A line by line explanation is as follows:

    1. Configures multiple APKs based on ABI.
    2. Enables building multiple APKs.
    3. By default, all ABIs are included, so use reset() and include to specify the APKs you want.
    4. Specify a list of ABIs for which Gradle should create APKs.
    5. Specify that you want to also generate a universal APK that includes all ABIs.
    6. Configure multiple APKs based on screen density.
    7. Turn off density-based APKs; set to true to turn on.
    8. Exclude these densities when building multiple density-based APKs.
    9. Map for the version code that gives each ABI a value.
    10. Iterate over all artifacts that are generated, and assign each with proper versioning.

    Once you have added this new code, sync the project with your Gradle files. Next, go to the Build top menu option and click MakeProject:

    Make Project

    When the make process finishes, navigate to the app/build/outputs/apk/debug/ folder in your Project view to see the split .apk files by platform types:

    Split APKs

    Also, here are the size differences:

    Before and after apk split

    For a simple app like this, the size savings are small, but they can be more significant for larger projects.

    Good job!

    Building Flags for Profit

    Often times, you will have a requirement in which you need to modify the behavior of your app based on the type of build. For example, for your debug build, you might want to enable logs but disable logging in your release builds, or simply use a different endpoint URL for a different build type.

    You can use Gradle to handle these requirements pretty gracefully.

    You are able to pass variables from your build to both Java and Kotlin code and to native code:

    Java and Kotlin Code

    The Gradle plugin provides a feature called the buildConfigField, which, once declared inside build.gradle for a particular build type, is generated as static properties of the BuildConfig class. The BuildConfig class is a special class that is generated by Gradle at build time. It contains other information like version, version code, etc.

    Give it a spin and declare your own variables. Add the code below to your debug build type, replacing // TODO: Declare debug specific buildConfigField and // TODO: Add app_type string resource here with value specific to debug type. Then define the equivalent for release block:

    buildTypes {
        ...
        debug {
           // 1 
           buildConfigField "String", "SERVER_ENDPOINT", '"http://www.myendpoint.dev.com"'
           buildConfigField "boolean", "ENABLE_LOGS", "true"
           buildConfigField "String", "PLAYTIME_STARTED", '"No"'
    
           // 2 
           resValue "string", "app_type", "Debug"
        }
        ...
     }
    

    Here you:

    1. Define your buildConfigField variables and give them values.
    2. Add a new generated resource, in particular, a string resource item generated at build time.

    Likewise, you can do this for other build types and modify the values.

    Next, sync your project and Gradle files. You can now access the variables you defined in your build.gradle file, via the BuildConfig class file in your Java and Kotlin code.

    Navigate to your MainActivity.kt file and replace // TODO: Append custom defined build variables to the textToDisplay, inside btnLetsPlay.setOnClickListener lambda with:

    textToDisplay.append("Playtime started : ${BuildConfig.PLAYTIME_STARTED}").append("\n\n")
    textToDisplay.append("Android logs enabled : ${BuildConfig.ENABLE_LOGS}").append("\n\n")
    textToDisplay.append("Server endpoint: ${BuildConfig.SERVER_ENDPOINT}").append("\n\n")
    textToDisplay.append("App type: ${getString(R.string.app_type)}").append("\n\n")
    

    Build and run the app. Click the Let’s Play button. You will see the following:

    Build Flags

    Native Code

    Accessing flags defined at build time from your native code — i.e., C/C++ code — is a bit tricky. To understand the process, you will need to understand how the native code is compiled.

    Your native code is first passed to a compiler — i.e., gcc/clang — and then is passed on to the CMake tool, which adds other shared libraries to the classpath. So to be able to pass a flag to your native code, you need to pass an argument to the compiler first. To do that, Gradle provides the externalNativeBuild block.

    Now, based on what kind of toolchain you are using to compile your native code, you can either have ndkBuild or cmake set up for you. In the current example, you are using the CMake toolchain.

    The way you pass arguments to the compiler is defined in the help manual for the compiler (clang):

    -D<macroname>=<value>
      Adds an implicit #define into the predefines buffer which is read before the source file is preprocessed.
    

    To pass values to your native code, you will use something like the following:

    // Use this to pass arguments to your C++ code:
    cppFlags.add("-D<macroname>=<value>")
    
    // Use this to pass arguments to your C code:
    cFlags.add("-D<macroname>=<value>")
    

    You can read more about these here.

    Open your build.gradle file for the app module and add the below code under your build_type/externalNativeBuild/cmake, replacing // TODO: Declare flags to be passed to native code, where build_type can be any of your build types such as debug or release:

    debug {
      ...
      externalNativeBuild {
        cmake {
          // Passed to your compiler i.e clang/gcc, hence available to your c/c++ code
          cppFlags.add("-DNDK_ENABLE_LOGS=true")
          cppFlags.add("-DNUMBER_OF_KIDS=3")
        }
      }
    }
    

    Sync the project with your Gradle files and navigate to app/src/main/cpp/native-lib.cpp via the Project view.

    Inside native-lib.cpp, replace // TODO: Add LOGD statements to log flags set at build time with the following snippet:

     LOGI("Number of kids: %d", NUMBER_OF_KIDS);
    
     // Flag shown in logs would be as per: true == 1, false == 0
     LOGW("Logs enabled: %d", NDK_ENABLE_LOGS);
    

    Now, build and run your app. Next, and click the Let’s Play button. Open up Logcat
    Logcat Tab

    Enter Native in the filter box to see the values as read from your native code, the values of which were defined in your build.gradle file:

    Native Logs in Logcat

    Wow, you are getting good at this! Great work!

    Configuring Modules

    Setting up different package names, resource prefixes, version suffixes for build types

    This is a pretty common use case. You have multiple build types and you want to define the package name, version name and debuggable properties on each one differently. Gradle has a solution for you.

    Add the following properties to your debug build types, replacing // TODO: Modify application id and version name and // TODO: setup debug flags:

    buildTypes {
        ...
        debug {
          // 1
          applicationIdSuffix ".debug"
    
          // 2
          versionNameSuffix "-debug"
    
          // 3
          debuggable true
    
          // 4
          jniDebuggable true
    
          // 5
          renderscriptDebuggable false
          ...
       }
       ...
    }
    

    A line-by-line explanation:

    1. The Application ID suffix is appended to the “base” application ID when calculating the final application ID for a variant.
    2. The Version name suffix is appended to the “base” version name when calculating the final version name for a variant.
    3. Set whether this build type should generate a debuggable APK.
    4. Set whether this build type is configured to generate an APK with debuggable Native code.
    5. Set whether the build type is configured to generate an APK with debuggable RenderScript.

    By modifying these in your build types, you can change the values accordingly per build type.

    Now, sync your project and Gradle files, and navigate to app/build/intermediates/manifests/full/debug/universal/ via the Project view.

    Next, open the AndroidManifest.xml file in this folder. This is the generated and merged manifest file for this build type. Here, you will see the changes in package name, version name and the debuggable flag being set in the manifest file:

    Debuggable Flag

    Also, if you build and run the app, then click the “Let’s Play” button, you can see the values in the text views inside the app:

    Packagename Suffix and debug flag

    If the build fails when you try to build and run the app, try cleaning the project by choosing Build ▸ Clean Project from the Android Studio menu.

    Speeding Up Your Build Times

    If you have been working with Android for awhile, you are aware of huge build times that Android projects can sometimes have.

    Huge build times like these

    Yeah, sometimes it can get out of hand. :]

    You can fix this and speed up your builds by tweaking your Gradle settings a little bit.

    Navigate to the gradle.properties file of your project via the Project view:

    gradle.properties file

    Append the below flags for the Gradle build system:

    # 1
    org.gradle.jvmargs=-Xmx2048m
    
    # 2
    org.gradle.daemon=true
    
    # 3
    org.gradle.configureondemand=true
    
    # 4
    org.gradle.parallel=true
    
    # 5
    android.enableBuildCache=true
    
    # 6
    org.gradle.caching=true
    

    Here’s an explanation, line-by-line:

    1. Specifies the JVM arguments used for the Gradle Daemon. The setting is particularly useful for configuring JVM memory settings for build performance.
      • -Xmx2048m: Increase the amount of memory allocated to the Gradle Daemon VM to 2 Gb.
    2. When set to true, the Gradle Daemon is used to run the build. The default is true starting with Gradle v3.0.
    3. For Gradle to know exactly how to build your app, the build system configures all modules in your project, and their dependencies, before every build — even if you are building and testing only a single module. This slows down the build process for large multi-module projects. Setting this field will make Gradle attempt to configure only necessary projects.
    4. When set to true, this will force Gradle to execute tasks in parallel as long as those tasks are in different projects.
    5. When set to true, this will force Gradle to not run a task if its inputs and outputs are equivalent to what they were during its previous execution.
    6. When set to true, Gradle will reuse task outputs from any previous build, when possible.

    With these settings enabled, your builds should be considerably faster. Build and run your app:

    Successfull Build

    Apart from the first build, all subsequent builds should be faster than the last one; the build time for your app would be much reduced. That’s Gradle magic for you. Enjoy this huge time saver for playing more video games!

    Another way to reduce your build times: Offline Mode

    If you are on a slow network connection, your build times may suffer when Gradle attempts to use network resources to resolve dependencies. You can tell Gradle to avoid using network resources by using only the artifacts that it has cached locally.

    To use Gradle offline mode:

    1. Open the Preferences window by clicking File ▸ Settings (on Mac, Android Studio ▸ Preferences).
    2. Search for “offline.”
    3. Check the Offline work checkbox and click Apply or OK.

    Gradle offline mode

    After you have enabled offline mode, build and run your app. You will see your app up and running with a much faster build time.

    Successfull Build

    If you’re building from the command line, pass the –offline option.

    $ ./gradlew build --offline
    

    When using offline mode, if you add a new dependency to one of your modules, you’ll need to make sure your connected to a network and disable offline mode temporarily in order to sync the new dependency.

    Playing With Product Flavors

    Often, you would like to have different versions of your Android app for different audiences, such as Free, Pro, Beta, etc. Gradle has a feature called Product Flavors, which represents different versions of your app that you may release to users.

    Furthermore, you can define different package names and version names for these flavors. To do so, you add the following snippet within the android block inside your app/build.gradle file, replacing // TODO: Add Product Flavors block here:

    android{
      ...
      // Add this block
      productFlavors {
        // Free Product Flavor
        free {
          applicationIdSuffix ".free"
          versionNameSuffix "-free"
        }
    
        // Beta Product Flavor
        beta {
          applicationIdSuffix ".beta"
          versionNameSuffix "-beta"
        }
    
        // Pro Product Flavor
        pro {
          applicationIdSuffix ".pro"
        }
      }
      ...
    }
    

    Product flavors, by default, combine with build types to generate Build Variants, which are nothing but combinations of your product flavor with build type, e.g., freeDebug, freeRelease, betaDebug, etc.

    Now sync the project with your Gradle files. Open the BuildVariants tab in Android Studio and switch to the freeDebug variant:

    Build variants

    Check out the few changes that you added in your product flavors. Navigate to app/build/intermediates/manifests/full/free/debug/universal/ via the Project View and open AndroidManifest.xml. This is the generated and merged manifest file for freeDebug build variant. Here, you see the changes in package name and version name in the generated manifest file.

    Note how the package name now has .free.debug appended and version name has -free-debug appended:

    freeDebug Variant

    Now, build and run the app. Click the Let’s Play button. You will see:

    Successfull Build

    Increasing Productivity With Gradle

    Inspecting the dependency graph

    Sometimes, you just want to inspect the dependency graph of your project. Gradle has just the right thing for you called the androidDependencies tasks. It generates a graph of all dependencies your project pulls in, even transitive ones.

    First, open a Terminal:

    terminal tab

    Now, execute the following in the Terminal, passing the androidDependencies task to the Gradle wrapper:

    $ ./gradlew androidDependencies
    

    You will see the following output:

    dependency graph

    It is also possible to execute this Gradle task from the UI in Android Studio. Simply open the Gradle side tab on the right-hand side and navigate to your_project/Tasks/android/ and double-click androidDependencies. This will execute the task:

    android dependencies

    You will see the output in the Run window:

    output in run window

    Testing Builds via Dry Run

    There are times when you may wish to see all the tasks that will be executed during the build, but you don’t want to execute them. This is a so-called dry run of your build. You can use the dry run of a build to see if the task dependencies are defined properly.

    To execute this, from the Terminal, pass the –dry-run option to Gradle wrapper, as shown below:

    $ ./gradlew build --dry-run
    

    You will see the build task execute successfully but that all the sub tasks are skipped. That is because you only wanted to dry run the particular task. You will see the following on a dry run execution:

    dry run

    Where to Go From Here?

    Phew, that was a lot of stuff, but you really did make it through all the training! Now, you are a master in the art of Gradle-fu!

    You can find the final project in the tutorial .zip file, which you can download using the Download materials button at the top and bottom of the tutorial.

    Gradle is a solid and very customizable build automation system. It makes the life of developers so much better by handling a lot of the nuances around the build system in a graceful manner — making the build system a joy to work with.

    Hopefully, after finishing this tutorial, you have a solid understanding of how you can leverage the powerful features provided by Gradle and the Android Studio Gradle plugin to make your development/build cycle more approachable and easier to work with.

    If you want to learn more about working with Gradle for Android Development, checkout Google’s official documentation.

    I hope you enjoyed this tutorial on exploring Gradle tips and tricks for Android Development. If you have any questions or comments, please join the forum discussion below!

Contributors

Comments