Saturday, 25 May 2013

Adding support for AndroidAnnotations in Gradle projects

Recently I've started migrating my Android projects to the new Gradle build system, and I've been quite impressed. The new build system is quite powerful, but at the same time it is rather fresh, which means there are some things that aren't supported or documented yet.

One of the things that took me quite a bit of time to research and figure out was adding support for the AndroidAnnotations project. While it seems like the annotation building is enabled out of the box, this specific library has some characteristics that only make it work after some custom configuration. One of the main issues was the fact that the library is looking for the Android manifest file by going up from the generated files. And considering that the Gradle project structure is different compared to the old one, this causes issues.

In the old project structure you would have something like this:

/
- AndroidManifest.xml
- src/com/example/project/MainActivity.java
- out/classes/*

As you can see, the generated annotation classes would be generated in a subdirectory of the project, and the main manifest file is in the project root. Now consider the gradle folder structure:

/
- build.gradle
- build/classes/*
- src/main
  - AndroidManifest.xml
  - java/com/example/project/MainActivity.java

As you can see, if we go up the folder structure from the generated classes we'll never get to the manifest file.

At this point there are two ways to make it work that I've found

  1. Add a "androidManifestFile" java annotation processor key that will point to the manifest file path.
  2. Change the output directory of the annotation processor so that the generated classes are stored alongside the rest of the source code.

In theory both are acceptable and will work, but I've opted for the second one, since this will make the generated classes visible in the IDE, and will help with syntax highlighting (if the classes aren't visible to the IDE, it complained about them, and made development somewhat cumbersome :) )

Adding this is relatively easy, thanks to the bugfixes introduced in the android gradle plugin since 0.4.1 (I had to jump through hoops to make it work befor

In my case, I've opted for the second option so I added a new task to my gradle build file that will ensure the output folder for the annotated classes exists, and wired in the annotation attributes to the javaCompile task for all the applicationVariants:

def annotationDirs = file('src/main/java-agen')

task annotationsDir {
    outputs.dir annotationDirs

    doFirst {
        if (!annotationDirs.isDirectory()) {
            println 'Creating: ' + annotationDirs
            annotationDirs.mkdirs()
        }
    }
}

tasks.clean.dependsOn tasks.cleanAnnotationsDir

android.applicationVariants.each { variant ->
    variant.javaCompile.dependsOn annotationsDir
    variant.javaCompile.options.compilerArgs += [
        '-s',annotationDirs
    ]
}