maven


My Gradle Tips and Tricks

gradle_logo

The new Android build system is based in Gradle, an advanced build tool that simplifies build automation.

I used Maven for many years and Gradle project structure is very similar, but Gradle configuration files are Groovy-based and much (much!) more simple. Here is the official documentation

This week I migrated a lot of my Android and GWT projects to Gradle (some of them in my github), and here are some tips that I discovered (and I will add more as I find them):

Embrace the new Source Structure

Initially can be difficult, but putting the code under /src/main/java/, Android resources in /src/main/res/, etc. is a very popular form to organize the code. You can configure the build.gradle file to keep the old Android project structure, but I suggest migrating to the new structure.

Reference Another Project in your Workspace

You have two options:

  1. Use a Multi-Project Build
  2. Install the artifact (JAR if is a standard Java library, AAR if an Android Library) in your local Maven repo

Both options are explained below:

Use Multi-Project Builds

You can create a multi-project setup with projects on subfolders adding a settings.gradle file at the root with a include statement:

include 'library-project', 'your-project'

Then inside a your-project’s build.gradle you can easily add a dependency to another project (called “other_project”):

dependencies {
    compile project(':library-project')
}

Sample here.

Install a JAR to your Local Maven Repo (with the source code)

With standard java libraries and a build.gradle file like:

apply plugin: 'java'
apply plugin: 'maven'
group = 'com.company'
version = '0.8'

You can do “gradle install” to copy it yo your Maven repo.

Manually Install a JAR to your Local Maven Repo (without source code)

At the moment a lot of Google artifacts (=JARs, AARs) are not available at the Maven Central repo, but you can install them manually to your local Maven repo

mvn install:install-file -Dfile=android-support-v4.jar -DgroupId=com.google -DartifactId=android-support-v4 -Dversion=0.1 -Dpackaging=jar

You can also do it with gradle, but it is a bit more complex sample here.

Reference an Artifact from your Local Maven Repository

Be careful to add mavenLocal() to your repositories in the build.gradle:

repositories {
    mavenLocal()
}

Then use a normal reference, for the android-support-v4.jar sample above, it will be:

dependencies {
    compile 'com.google:android-support-v4:0.1'
}

Install an AAR to the Local Maven Repository

AAR is the new package format for Android Libraries. The “gradle install” task (that should install the AAR in the local Maven repository) does not works as expected in those projects, so I use a little hack:

apply plugin: 'maven'
uploadArchives {
  repositories {
    mavenDeployer {
      repository url: 'file://' + new File(System.getProperty('user.home'), '.m2/repository').absolutePath
    }
  }
}
task install(dependsOn: uploadArchives)

Usage sample here.

Android Library Projects need an Application Tag

This is an ADT bug, and the workaround is to add an empty application tag yo your library’s AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
    <application />

Include all the JARs in the libs/ Directory

This was the default behaviour of Android Projects, to maintain it you can add to your build.gradle:

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
}

I suggest referencing local JARs only in final Android projects, referencing them in libraries can cause problems, read below:

Avoid Referencing Local JARs in Android and Java Libraries

If you reference local JARs in Android libraries, those jars are going to be embedded in the AAR. If you reference again the JAR from a project that uses the AAR you will see this build error:

:project:dexDebug
UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: already added: Landroid/support/v4/app/ActivityCompatHoneycomb;
//..
1 error; aborting
:project:dexDebug FAILED

If an Android Library projects references a JAR library, and the JAR library references local JAR files, those local JAR files are not going to be embedded in the Android projects using the library.

The best solution is to install all the JARs to your local Maven repo as explained above.

Or Embed all the Dependency JARs Into your Library

In some situations you may embed all the JARs in your library, but ONLY if those dependency JARs are not going to be referenced again in a project that uses the library.

jar {
    from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}

Android Projects Need Libraries Compiled with Java 1.6

If in an Android Project you use a library JAR including classes compiled with source compatibility to Java 1.7, build gives this WARNING:

trouble processing:
bad class file magic (cafebabe) or version (0033.0000)
...while parsing Class

…and the class is not included in the APK, so the app force closes at running.

This may be solved adding in the build.gradle of the library:

apply plugin: 'java'
sourceCompatibility = 1.6
targetCompatibility = 1.6

Create a Source JAR for a Library

Source JARs are needed for libraries used in GWT projects. To generate them add this to your build.gradle:

apply plugin: 'maven'
//...
task sourcesJar(type: Jar, dependsOn:classes) {
    classifier = 'sources'
    from sourceSets.main.allSource
}
artifacts {
    archives sourcesJar
}

You can install them to your local repo with “gradle install”

Include a Source JAR from Another Project

For multi-project_builds:

dependencies {
    compile project(':other_project')
    compile project(path: ':other_project', configuration: 'archives')
}

Sample here.

Include a Source JAR from a Maven Repo

Simply append “:sources”:

dependencies {
    compile 'com.company:artifact-name:0.8'
    compile 'com.company:artifact-name:0.8:sources'
}