Playing with Gradle: Basics

Playing with Gradle:

Chapter 1: Basics

Chapter 2: Android Tasks Basics

Chapter 3: Flavors, BuildTypes and Variants

Chapter 3 1/4: Flavors And Manifest

Chapter 3 and some few: Understanding the life cycle.

Chapter 3 and some few more: Code Coverage on Android with Jacoco

Let’s have a look at the build system use for developing Android application.

1 Gradle in the project

In our Android project Gradle is set in the following way:

Yep, gradle is everywhere, scaring isn’t it?

By the way there are 2 different types of files:

· Those that describe the project

· Those that describe the gradle’s configuration to use for this project.

Let’s beginning by the gradle’s configuration.

1.1 Gradle’s configuration files

1.1.1 What Gradle’s version should I use

The first parameter you have to define is which version of Gradle you want to use. In a way, it’s pretty natural and well thought, don’t you think?

The files concern are those ones:

Which is the gradle folder of the project but more specifically is where you define the wrapper to retrieve the version of gradle you want to use.

To define the gradle version, we just define where is the version of Gradle to use (and download if it’s missing) in the gradle-wrapper.properties:

#Mon Dec 28 10:00:20 PST 2015
 
distributionBase=GRADLE_USER_HOME
 distributionPath
=wrapper/dists
 zipStoreBase
=GRADLE_USER_HOME
 zipStorePath
=wrapper/dists
 distributionUrl
=https\://services.gradle.org/distributions/gradle-3.3-all.zip

In this file you just have to change the url to the version you want to use. You can have a look here to see what is the last version number: http://services.gradle.org/distributions

A good rule for that is to have the latest version of gradle because the gradle team works hard to optimize your builds from version to version.

1.1.2 How can I build this type of project?

The second element to configure is what type of project do I build and more specifically how can I build them? Because each type have specific tasks and way to do the build. Those specific tasks are delivered through gradle’s plugins. So we need to explain to gradle where it can find the plugins it needs.

We do that in our root build.gradle file. We call it like that because it is at the root of your project and defines properties for the project and all its subprojects.

This file is the following

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
allprojects {
    repositories {
        jcenter()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}

You have 3 different parts:

· The buildscript where you define where the repositories are and what is the names of the gradle’s plugins to use for this project.

· allProjects define for all the project and its sub-projects where to download the libraries it needs

· task is just a new task added to your gradle task that just clean the build directory

So here, we just define that we want to use com.android.tools.build:gradle:2.2.3 as plugin for gradle to build our project. It means the tasks of this plugin will be available for building your project.

1.1.3 How can I tune gradle (memory and more)?

In the file gradle.properties you can define a lot of properties for your project (and its sub-projects).

We often use this file to define the memory dedicated to the gradle daemon.

# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
#org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Enable daemon
org.gradle.daemon=true
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Try and findout the best heap size for your project build.
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# Modularise your project and enable parallel build
org.gradle.parallel=true
# Enable configure on demand.
# avoid building part of the project when it's not necessary
org.gradle.configureondemand=true

In this example we have define that:

· the configuration on demand is enable

· we run the build in parallel

· we want 2048 M of JVM for the daemon

· we want the daemon to be activated

So we tune our gradle process depending on our computer to optimize it.

We will see that file is also use to define others types of variables that will be accessible by the subprojects and the gradle.build files.

1.2 Project’s configuration files

So we have explained to gradle how to work, let’s explain to it what to do.

1.2.1 How many modules belong to your project?

We first describes what contain our project. This is done in the setting.gradle file:

And it only contains the list of your sub-projects:

include ':app'

1.2.2 What is my module(s) description?

Now we are ready to define our Android project. This description is done in the file build.gradle at the root level of your module.

And it contains:

apply plugin: 'com.android.application'
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.android2ee.basile.multiplication"
        minSdkVersion 10
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.0'
    testCompile 'junit:junit:4.12'
}

This file contains 3 main parts:

· What is the plugin to use to build the module: we want android plugin

· What is the android description of the module: the android block

· What are the libraries I need to build it: the dependencies block

The android block defines essential elements of your Android application:

· compileSDK: what version of the SDK do I use for compilation, higher is better

· minSDK: What is the minimal SDK version am I compatible with. A good rule is to follow GoogleServices minSDK

· targetSDK: What is my running environment of predilection (If I run in a higher version of it, the system will emulate my target version has execution environment).

· versionCode : Auto-increment integer that follow the delivered version

· versionName: Human readable version

· applicationId: We used to have the root package of the application as applicationId and to define it in the manifest.xml. Now you can keep your root package in the manifest (it will be use by the device when running your application for the classpath) and have a different applicationId in your build.gradle (it will be used by PlayStore and Device to identify your application). That means you can refactor your packages and keep your applicationId.

· testInstrumentationRunner defined which instrumentation runner you want to use when running your test. It’s related to the AndroidTestingSupportLibrary Sdk and give you the ability to use a Junit version 4.

The build type block gives you the ability to specify for each build type specific elements (like versionNameSuffix, applicationIDSuffix, minify, shrink…).

By default you have 2 build types : Release or Debug. And you should stick on them because they are the way to build your application. If you want to customize your application according to a context (client, processor, brand, free/paid,…) you should consider flaviors.

The dependencies blocks is here to describes what are the libraries you depends on. You can specifiy if it’s for compilation, test, for which flavor… and so on. Let’s have a look:

compile fileTree(dir: ‘libs’, include: [‘*.jar’]) explains that all the files contained in the folder libs that ends with .jar should be included for compilation of the apk,

androidTestCompile(‘com.android.support.test.espresso:espresso-core:2.2.2’, {
 exclude group: ‘com.android.support’, module: ‘support-annotations’
 
})
 explains that when running for instrumentationTest we will need espresso but we will exclude the support-annotations modules from it (transitive dependencies)

compile ‘com.android.support:appcompat-v7:25.1.0’ explains that we use the artifact with groupid=com.android.support, artifactId=appcompat-v7 with version 25.1.0. Which is the normal coordinate of any project in maven repositories.

testCompile ‘junit:junit:4.12’ just explains that for unit tests we need Junit 4.12 expressed in maven’s project coordinates.

1.3 Gradle with the command line

Here is the last files you find in your project gradlew and gradlew.bat which are the gradle worker executable.

This is the command line tool (one for linux/mac, the other for windows). That means you can run in command line gradle command directly from Android Studio like that:

As you see, use ./gradlew (relative path from the console point of view) because gradlew is not a global command for your computer.