Gradle And Android
I’ve been wanting to play with Gradle for some time. I’m currently working on a fairly large Android project, deploying multiple applications, doing some advanced testing, and sharing a fair bit of code between applications.
Our project is currently using Maven, which has some significant drawbacks. My primary issue is that it’s a very opaque system, and you have to really study it to understand it properly. When maven fails to do what you’d expect, it’s often rather challenging to determine a root cause and potential fixes. Secondly, XML feels inordinately verbose for this task. Laying out a simple dependency is a minimum of 4 lines of XML, making readability quite hard.
Given that Google is now promoting Gradle as their new build system, I figured now was a good time to try the conversion. Here are my experiences so far.
IntelliJ 13
JetBrains announced Version 13 of IntelliJ, along with Android Studio, the open source version, in concert with Android’s launch of the new build system. Over the past weekend, I plunked down my $99 upgrade fee to get to this new version. Unfortunately, version 13 (and 13.0.1) do not support the Gradle Android plugin at v0.7.+. You get an error: “Cause: java.util.ArrayList cannot be cast to java.util.Map”. Apparently, 13.0.2 will fix that problem.
AAR and APKLIB
Sharing code between Android applications is a bit tricky. The way the android maven plugin did it was fairly straight-forward. You indicated that your project’s artifact was an “apklib”. After building, this zips up your sources, your libs native directory, and your /res directory into a jar file. Note that it does NOT contain any compiled classes.
Gradle’s Android plugin does not support this. Rather, it defines a new format, called an AAR, presumably for “Android Archive”. This format DOES include the compiled classes, but does NOT include your sources. This is arguably a better approach. However, all of your dependent projects that you have been including via APKLIB will need to be converted to AAR BEFORE you can build your actual Android application in Gradle.
Testing
At the moment, the Android gradle plugin does not have any support for testing. This means that you will not be able to run “gradle test” to run your unit tests. Luckily, Jake Wharton has created the android-test plugin.
Unfortunately, it seems Robolectric and the gradle-android-test-plugin don’t exactly get along, which means that you have to use snapshot versions of both. At the time of this writing (January 19th, 2014), the android-test-plugin was at 0.9.1-SNAPSHOT and Robolectric was at 2.3-SNAPSHOT.
I then got an error that Robolectric required the android support library, specifically “com.android.support:support-v4:19.0.0”. After much poking around, it turns out that you have to open up the Android SDK tool, and install the “Android Support Repository” (refer to the Gradle and Android Support Library link below). Once that’s installed, the Gradle Android plugin automatically picks it up. A bit of unnecessary magic, and it should be in the documentation.
In the same vein, to use google play services, we also need to use the Android tools to install the Google Repository. This includes the play-services aar files, which again, are magically picked up by the Gradle Android plugin. It uses the ANDROID_HOME environment variable to figure that out.
Publishing An AAR
Typically, you’d run gradle install
, and that would install your
plugin to your local maven repo. Not in this environment. This is
because the Gradle Android plugin overrides the install
task to
install your application to an Android device. I found some good information
about using the maven-publish
plugin. You use bundleRelease
as the artifact to be published, which
is available once you run the gradle task android.libraryVariants
. As
of the latest Android Gradle plugin, however, the order that task is
available was changed, and it no longer works.
Ultimately, I wound up hard-coding the asset that gets created. I create an artifact name like this:
def artifactName = "build/libs/${artifactId}-${project.version}.aar"
Then used maven-publish like this:
publishing {
publications {
maven(MavenPublication) {
artifact file(artifactName)
}
}
}
Once that’s done, I can run gradle publishToMavenLocal
, and everything
is happy. I’m assuming there are better ways to do this, and like most
of what I’m saying, I’m also assuming (hoping!) that this technique has
a short shelf life. In other words, having to hard-code the artifact
name is yucky, and I don’t want to have to do that in the future.
Versions
This all feels quite bleeding edge. Many of the posts, Stack Overflows, etc, did not include versions of all this components that work together. That seems to be the biggest problem at the moment. It’s changing quickly, and at any one point in time, the latest versions may or may not work with others. I’ll publish the versions of everything I was trying to use.
- Gradle: 1.9 (tried 1.10, Android plugin doesn’t support it).
- Gradle Android Plugin: 0.7.3
- Gradle Android Test Plugin: 0.9.1-SNAPSHOT
- Robolectric: 2.3-SNAPSHOT
- IntelliJ 13.0.1 - Currently doesn’t work with Android Gradle Plugin 0.7. 13.0.2 will, according to this bug report
Overall Impressions
Maven vs Gradle: I much prefer Gradle’s groovy-based syntax. Having a
DSL feels like the right way to define a build system. However, my
concern about the “magic” of maven has not been allayed. Gradle, along
with the many plugins, has tinges of the same magic. The example of
gradle install
is a good one. We all expect the install
task to
install the artifact into the repository. The fact that the Android
plugin can override that is either a bad design choice by that team, or
possibly a bad choice by Gradle in allowing core tasks to be
overridden.
It feels super-early days for all of this. I want a development environment that gets out of my way, and lets me deliver value for my time. Time is my most precious asset, and spending time fiddling with versions of this or that and finding the next bug feels quite fragile. Google and JetBrains have promised a lot with their “New Build System”, and I think it has promise, but it feels a long way off before it’s stable and repeatable.
I’ll update this post as I learn more and use it more. I have a number of hours into this project, and so far I only have one aar in my local maven repository. I have not yet dealt with Assembling the app, signing the app, working on different “flavors” of the app, etc. There’s still more to do and learn.