Clean Android Code: Building with Gradle

Clean Android Code articles series:

  1. Clean Android Code: Main
  2. Clean Android Code: Building with Gradle
  3. Clean Android Code: Preparation
  4. Clean Android Code: Activities & Fragments
  5. Clean Android Code: Network & Data
  6. Clean Android Code: Navigation & UI
  7. Clean Android Code: Event Bus
  8. Clean Android Code: Error Handling
  9. Clean Android Code: ViewPager with View Pages
  10. Clean Android Code: Clean Adapter
  11. Clean Android Code: …

Gradle is a build system that was introduced to Android a couple years ago. If you are still not using it — you should. The only downside I see is that Groovy is used to write scripts, but, luckily, there is already an initiative to bring Kotlin to Gradle. It is still under development, so for now let’s stick with defaults.

Project Configuration

When you start a new project, it is important to keep your build system clean, so you could easier tweak things here and there during development.

First important file you should modify is gradle.properties.

org.gradle.parallel=true
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
kotlin.incremental=true

Modifying it this way will speed up build process by giving gradle more memory, letting it run in parallel and using incremental compilation that was introduced in Kotlin 1.0.2

build.gradle in the root of our project is a global one, so let’s define some global settings there:

ext {
versionName = "1.0" //default version name
versionCode = 1 //local build version code
    //android build settings
androidBuildToolsVersion = "23.0.2"
androidMinSdkVersion = 16
androidTargetSdkVersion = 23
    //packages for our project
androidApplicationIdStage = "pro.averin.anton.clean.android.cookbook.stage"
androidApplicationIdProduction = "pro.averin.anton.clean.android.cookbook"
}

It’s also a good idea to put all links to maven and all global dependencies into buildscript right here, and remove it from all internal build.gradle files so we would have a single place to edit it later on.

We also apply Kotlin plugin, you can either do it via Android Studio IDE, or just put things into gradle manually

buildscript {
ext.kotlin_version = '1.0.2'
repositories {
mavenCentral()
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url "https://plugins.gradle.org/m2/" }
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.android.tools.build:gradle:2.0.0'
classpath 'io.fabric.tools:gradle:1.+'
}
}

Another nice addition to a build system I like to do is moving all dependencies into a separate file dependencies.gradle that would look something like this:

ext {
supportLibraryVersion = ‘23.4.0’
daggerVersion = ‘2.1’
rxVersion = ‘1.1.0’
//… etc
dependencies = [
appCompat : “com.android.support:appcompat-v7:$supportLibraryVersion”,
recyclerView : “com.android.support:recyclerview-v7:$supportLibraryVersion”,
cardView : “com.android.support:cardview-v7:$supportLibraryVersion”,
supportV4 : “com.android.support:support-v4:$supportLibraryVersion”,
supportDesign : “com.android.support:design:$supportLibraryVersion”,

daggerCompiler : “com.google.dagger:dagger-compiler:$daggerVersion”,
dagger : “com.google.dagger:dagger:$daggerVersion”,
rxJava : “io.reactivex:rxjava:$rxVersion”,
rxAndroid : “io.reactivex:rxandroid:$rxVersion”,
rxPermissions : “com.tbruyelle.rxpermissions:rxpermissions:$rxPermissionsVersion@aar”
//… etc
]
}

This will let us keep build.gradle file clean, and we will have a single place to manage all of the project dependencies.

You would need to apply this file in your main build.gradle, like this:

apply from: ‘buildsystem/dependencies.gradle’

There is another big addition to the build system I would like to make. You do it once and benefit throughout the whole project. It’s a Jacoco coverage — click on the link to see more details.

Jacoco

For Jacoco, I add 2 new tasks per flavour — one would be calculating code coverage for the whole project, and another one will compare your current branch with either develop or target branch you merge into(when on Jenkins), and calculate coverage only on classes that you have changed. This helps tremendously when you push new code and want to make sure that code is covered to maximum.

Full coverage can be run by ./gradlew testStageDebugUnitTestCoverage

Difference coverage can be run by ./gradlew testStageDebugUnitTestCoverageDiff

Helpers

A small VersionCodeReader class with help you a little when you will be integrating your project with Jenkins for Continuos Integration

class VersionCodeReader {
static def getVersionCode(defaultVersion) {
def code = System.env.BUILD_NUMBER != null ? Integer.parseInt(System.env.BUILD_NUMBER) : defaultVersion
println code == defaultVersion ? "BUILD_NUMBER not found. Falling back to default version code: $defaultVersion" : "VersionCode is set to $code"
return code
}
}

It falls back to a default version for local builds, and uses a proper build number on Jenkins. Note, that if you plan to build local release builds you will have to keep track of versions manually.

Also, please don’t forget to enable multi dex. You would say this is a bit too early, but keep in mind that Instant Run (even though it’s not yet fully compatible with Kotlin) adds 3–5 times method count to your project just to be able to run it, so you will need MultiDex for sure if you plan to use Instant Run, and we also have significant number of libraries already. Easier to do it now rather then trying to figure out why do I get this error? later

And databinding should also be enabled.

android { ...
   dataBinding {
enabled = true
}
}

Signing

For release build on Android we need to have a release key to sign our application. It’s a good idea to keep your signing key safe.

The usual recommendation I saw was to either use system variables, or to use gradle.properties in user’s home folder. But the problem is that there is a big chance you will end up having different release keys for different projects, or, at least, different alias in the same keystore — so it would be good to have per-project settings.

In your buildsystem folder create a new file, call it, for example, secret.gradle and put your release key settings there :

ext {
    keystore = "<path to keystore>"
keystorePass = ""
alias = ""
aliasPass = ""
}

Don’t add this file to source control and make sure to keep it safe, keep a copy in several secure places.

Now, when this file is present in the project and is applied in main build.gradle file, you can reference these variables in your gradle files, and if a new developer would fetch your project from github to make it work you would just share your secret.gradle file with him.

In addition to keystore settings you can keep private API keys here — I will show an example in Network&Data part of the series.

Now all you need to do is to apply your settings for signing a release

signingConfigs {
debug {
}
release {
storeFile file(globalConf.keystore)
storePassword globalConf.keystorePass
keyAlias globalConf.alias
keyPassword globalConf.aliasPass
}
}
...
buildTypes {
release {
signingConfig signingConfigs.release
}
}

Gradle is a very powerful tool, and this article is by no means an advanced guide to gradle — can’t really name myself an advanced gradle user even. I am sure there is plenty of more complex things you can do in gradle, but hope this small set of settings will help to keep your project clean.

You can see all the changes I made to the build system on Github: here, here and here, latest state will always be on develop branch.

Clean Android Code articles series:

  1. Clean Android Code: Main
  2. Clean Android Code: Building with Gradle
  3. Clean Android Code: Preparation
  4. Clean Android Code: Activities & Fragments
  5. Clean Android Code: Network & Data
  6. Clean Android Code: Navigation & UI
  7. Clean Android Code: Event Bus
  8. Clean Android Code: Error Handling
  9. Clean Android Code: ViewPager with View Pages
  10. Clean Android Code: Clean Adapter
  11. Clean Android Code: …