Make your build.gradle great again

Sergii Zhuk
ProAndroidDev
Published in
5 min readSep 16, 2016

--

In the previous post I shared some of best practices our team is using for the developer productivity, now it is time to focus on gradle-specific hacks to make your everyday Android developer life better.

#1 Use ‘apply’ to structure the script content

In Gradle apply command can be used to apply not only plugins, but also script files (*). In this way you can divide your main build.gradle file into smaller parts, and move extra tasks like jacoco report and findbugs to the separate files.

(*) Update: Gradle core developer Stefan Oehme commented that it is correct to call included scripts not “script files” but “script plugins” as they are just a lightweight way of writing a plugin.

#2 Set application id in the build file

Keep in mind the difference between applicationId and packageName. Android tools doc says the following:

  • The final package that is used in your built .apk’s manifest, and the package your app is known on device and in the Google Play store, is the “application id”.
  • The package that is used in your source code to refer to your R class, and to resolve any relative activity/service registrations, continues to be called the “package”.

Package must be always set in your AndroidManifest.xml file and application id is completely independent from it. But for compatibility reasons application id by default is equals to the package. It worth to set application id explicitly in your build configuration:

defaultConfig {
applicationId "com.example.demoapp"
// ...
}

There is a really unpleasant bug about application id that was reported to bug tracker almost one year ago, but hasn’t been fixed yet. The problem is that application id is not populated to the connected libraries if application id is not set in build.gradle. This issue could result in your app failing to install on the device from Play Store and report tools won’t be able to notify you about the problem.

#3 Setup a separate application id for debug and release builds

Applying applicationIdSuffix for your debug build will append a text to your applicationId. This small trick allows you to keep both production and development app builds on the same device.

buildTypes {    
debug {
applicationIdSuffix ".debug"
}
// ...
}

The code above will result to applicationId com.example.demoapp for the release app and com.example.demoapp.debug for the debug one.

#4 Build time matters

You probably know that adding each new library or module will increase the build time. I’m sure it makes sense to track the time spent on each build and note the increasing trend. It is up to you how to deal with this trend — maybe use no-op versions of some libraries or make a report to your boss why you need a new machine :)

The first thing you can do is to use --profile command which will help you to find gradle task slowing down the build. More advanced way is to use Build Time Tracker plugin which will help you continuously check the build time, generate .csv reports, and even collect build stats from various machines.

Update: Cédric Champeau from the Gradle team recommended to use Build Scans instead of “--profile”.

#5 Make a proper release configuration

Enabling Proguard option for the release build is available out of the box for a build file generated by Android Studio. But keep in mind that Proguard works on the Java side. Unfortunately, it doesn’t work on the resources side. As a consequence, if an image my_image in res/drawable is not used, Proguard only strips it’s reference in the R class but keeps the associated image in place. However, we can force gradle script to do that by setting shrinkResources to true. Don’t forget to test your release APK carefully after applying these tools.

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

}
// ...
}

You can go even deeper by splitting your app to several APKs, just make sure that you have capacity to test all these builds using different screen sizes/target ABIs.

#6 Find some useful gradle tasks or develop your own

Don’t forget that Gradle build scripts are written in Groovy, a general-purpose scripting language for the JVM. Gradle makes it possible to use a declarative programming style for the bulk of the build script, and the flexibility of a scripting language where needed. A nice set of useful gradle scripts was collected by reddit users on this page. My favourite ones are for code coverage check and setting version number to the app icon.

#7 Externalize your dependencies versions if you are using submodules

The common thing to do for the app with a lot of subprojects is to keep the same version of all repeating dependencies. Nishant Srivastava recently shared a nice workaround how to externalize your dependencies to the separate file and refer to them from build.gradle configuration of each subproject.

// root dependencies.gradle file:
ext {
supportLibVer = "24.2.0"
//...
}
// ---------------------------
// subproject root build.gradle file:
buildscript {
apply from: '../dependencies.gradle'
// ...
}
// ---------------------------
// subproject module build.grale file:
apply plugin: 'com.android.application'
// ...
dependencies {
compile "com.android.support:appcompat-v7:$supportLibVer"
compile "com.android.support:design:$supportLibVer"

// ...
}

Topics below have been already mentioned in my previous blog post about developer best practices, I’m adding them to make this post consistent.

#8 Get rid of mavenCentral

Get rid of mavenCentral and use jcenter as the dependencies repository. jcenter has faster response time and now acts as a superset of mavenCental content.

#9 Use minSdkVersion 21+ for debug

Use build flavors to setup the build for minSdkVersion 21 or higher during the development. It will help to build faster using all improvements that the Android Tools team provides for us.

productFlavors {
// Define separate dev and prod flavors
dev21 {
minSdkVersion 21
}
dev14 {
minSdkVersion 14
}
prod {
// The actual minSdkVersion for the production app
minSdkVersion 14
}
}

Thank you for reading and looking forward to hear your thoughts and your own best practices!

--

--