If you are an Android developer, you must have heard about the “new build system” by now, comprising of Gradle and a plugin to go with it, regardless of whether or not you have tried it.
I started using it almost a year ago back when it was released in an early alpha stage, mostly because it was the only way of supporting library projects with Gradle (which Jason Voegele’s plugin did not support). After its official unveiling at Google I/O in May 2013 and months of updates that followed, I can finally call it production-ready. Hence, I wanted to write a little tutorial explaining how to set up a new Android project with Gradle.
For those of you who are like me in not willing to switch to Android Studio just yet, the tutorial will also cover how to retain Eclipse compatibility after migrating to Gradle. Jump past the break for the full write-up.
Aside from Eclipse (which I assume you already know how to set up), the only thing you need is a Gradle distribution, which you can download from here. Check the release log for the plugin to find out the latest supported Gradle version.
Optionally, you can also install Gradle integration for Eclipse, which will bring syntax highlighting and code completion to your build scripts.
The easiest way to start a Gradle-based Android project is to create a new project in Android Studio, however if you do not intend to switch to it for good, this will not work as it is not backwards compatible with Eclipse. Instead, let us create a new project in Eclipse and then enhance it with Gradle.
In Eclipse, follow the usual process: File -> New -> Android Application Project, fill out the application name, project name and package name and click “Finish”.
Note: This section will explain enough Gradle for this sample app, however if you have never used it before, I would advise having a look at the documentation as you will quickly run into questions when you start integrating it into your own project.
Now that you have a new Ant-based project, we have to add a Gradle script manually. Create a new file called “build.gradle” in the root of your project.
Starting with the configuration for the build script itself, the repository notation mavenCentral()
pulls down the URL for the default Maven Central (for a custom repository use: maven { url 'http://repo.example.com' }
) and the only custom plugin we need is the Android Tools one:
buildscript {
repositories {
mavenCentral()
// maven { url 'http://repo.example.com/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:0.6.1'
}
}
Next we apply the plugins that we need to use in the build script. Notice that we only defined the Android one in the section above since the other two are available out-of-the box. The only configuration that we have in this sample app is “compile”, however you may need others later, e.g. “instrumentTestCompile” for instrumentation test dependencies etc.
apply plugin: 'maven'
apply plugin: 'android'
apply plugin: 'eclipse'
configurations {
compile
}
Similar to how we defined the repositories and dependencies for the build script, we now have to do the same for the whole app. Note how the first “compile” entry uses a local dependency from the “libs” directory in additional to the usual remote dependency notation.
Tip: Folks at Google have set up a convenient way of searching for remote dependencies by name and generating the Gradle syntax for them at http://gradleplease.appspot.com/.
repositories {
mavenCentral()
}
dependencies {
compile files('./libs/android-support-v4.jar')
compile 'org.apache.commons:commons-lang3:3.1'
}
The most important part is the Android clause, which has all the configs for the Android plugin.
Compile SDK and Build Tools versions depend on what you have installed locally, so check your “SDK Manager” if you can’t remember the versions.
Source sets define where to look for sources to compile, similar to the “sourceSets” in the Java plugin. Although you can define everything explicitly, you only need to define entries that are different to the default locations in Android Studio. For more on this, read the “File Structure” section below.
The default config section defines manifest properties that apply to all build types. Note that the values in your “AndroidManifest.xml” will be overwritten by the values defined here! In the signing config section you can define any number of keystores and corresponding credentials to use in different build types.
Finally, the build types define different build targets. These can be anything, not just the preset names and once they are defined here, Gradle will automatically create the corresponding build tasks for them.
android {
compileSdkVersion = 'android-18'
buildToolsVersion = '18.1.1'
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
}
defaultConfig {
packageName = 'net.gouline.android.gradle'
versionCode = 1
versionName = '1.0.0'
minSdkVersion = 8
targetSdkVersion = 18
proguardFile getDefaultProguardFile('proguard-android.txt')
}
signingConfigs {
release {
storeFile file('release.keystore')
storePassword 'android'
keyAlias 'releasekey'
keyPassword 'android'
}
}
buildTypes {
debug {
debuggable = true
runProguard = false
}
release {
debuggable = false
runProguard = true
signingConfig signingConfigs.release
}
}
}
Now we come to the most crowded part of the build script that I put together after poking around various bits of documentation for a long time.
First we add the configurations that we defined above to the list of Eclipse configurations, this will make all the dependencies available in the IDE.
Then we get to editing the classpath file manually, which unfortunately is necessary to adjust some details. Before the existing XML gets merged with the generated one, we remove all the entries so that there are no unexpected overlaps. Then while generating the new XML, we add entries for the non-standard source directories (otherwise, Eclipse assumes that your sources are in “src”). Finally, you can exclude any generated entries by name in case they are included locally (the example included assumes that the support library is pulled down as a remote dependency, which would duplicate the local copy in “libs”).
Finally, for the project file we set the project name that you will see in your project explorer and add the Android nature and builders to let Eclipse know that this is an Android project. And that’s it for the build script side!
eclipse.classpath.plusConfigurations += configurations.compile
eclipse.classpath.file {
beforeMerged { classpath ->
classpath.entries.removeAll() { c ->
c.kind == 'src'
}
}
withXml {
def node = it.asNode()
node.appendNode('classpathentry kind="src" path="src/main/java"')
node.appendNode('classpathentry kind="src" path="src/debug/java"')
node.appendNode('classpathentry kind="src" path="gen"')
node.children().removeAll() { c ->
def path = c.attribute('path')
path != null && (
path.contains('/com.android.support/support-v4')
)
}
}
}
eclipse.project {
name = 'AndroidGradleBasic'
natures 'com.android.ide.eclipse.adt.AndroidNature'
buildCommand 'com.android.ide.eclipse.adt.ResourceManagerBuilder'
buildCommand 'com.android.ide.eclipse.adt.PreCompilerBuilder'
buildCommand 'com.android.ide.eclipse.adt.ApkBuilder'
}
File structure is one of the things that changed between Eclipse and Android Studio, the latter obviously being a fork of IntelliJ, which has always had quite a specific Java structure. Easiest thing to do is adopting either one or the other, however if you want to be able to use either IDE, you will need to create a hybrid.
Eclipse is the more restrictive out of the two as Android Studio will work with almost any file structure you give it, provided that you describe it in the source sets of the build script. Here are the items that Eclipse won’t allow you to move:
AndroidManifest.xml
res/
assets/
As a result, we defined them separately in the build script described in the previous section. Everything we haven’t defined, must be in the default location where Android Studio would have looked for it:
To cover the first point, inside your “src” directory create a new directory called “main” then inside of that, another one called “java” and move your root source directory (“com”, “org” etc.) there.
This tutorial does not cover instrumentation testing so ignore the second point for now. Great, now you’re all set to try everything out!
Android Studio is the easiest one, you can just open the project in its current state and the IDE will take care of the rest.
For Eclipse, you need to first generate the project file, pull down remote dependencies and link them in the classpath file. You can do this by running the following terminal command from your project root directory:
gradle cleanEclipse eclipse
First task “cleanEclipse” cleans up everything previously generated to avoid any clashes, the second “eclipse” generates everything.
If you still have the project opened in Eclipse, delete it by clicking on the project name and then Edit -> Delete (ensure that you leave “Delete project contents on disk (cannot be undone)” unchecked, we only want to remove it from Eclipse not the face of the earth!). This step is optional but Eclipse doesn’t like you messing with its project and classpath files so leaving it open may cause unexpected errors.
Once completed, you can re-import the project: File -> Import -> General -> Existing Projects into Workspace.
Note: Do not use Android -> Existing Android Code Into Workspace as this will overwrite all the Eclipse hackery that the build script is doing and you will most likely get tons of cryptic errors.
That’s it! If you are seeing some build path issues, refreshing the project should fix it.
While you can still run the app from the IDE, being able to build in headless mode may be useful for consistency and continuous integration. To build a debug APK, you can run the following command from the project root directory:
gradle clean assembleDebug
You will see a directory called “build” appear, which contains the “apk” directory, where you will find the result of the build. You can build any target that you defined in the same way, e.g. “assembleRelease” for the release, in fact a new “assembleBuildType” task will be created for every build type that you define.
You can even install the APK as part of the build command by appending “installDebug” on the end.
And there you have a working Android project with Gradle! I will be publishing more tutorials expanding on this sample soon so watch this space. Please leave feedback below with any questions or corrections.