Building Android NDK projects with Gradle and Travis CI

I’ve migrated all of my Android projects over to Gradle now as that seems to be the direction that Android development is taking, but if you use the Android NDK in your projects then you’ll discover that NDK support is severely lacking in Gradle. There’s undocumented support for the NDK which can be found if you dive into the samples, but it doesn’t look to be too developed at the moment. Until there’s an official solution for this, I’ve taken my own approach to this, inspired by countless StackOverflow questions and answers.

I also use Gradle in combination with GitHub and Travis CI in order to build and test the latest commits automatically, so I’ll share the .travis.yml build script that I use to do this.

This may not be the best way of doing this, and it will probably be made obsolete when an official solution is documented. However, it works for me. If you know of a better solution then please leave a comment.

Gradle

If you’re new to Android development or you’re migrating from an ant build script to Gradle, then I suggest you get started by taking a look at existing projects and making sure you can build a basic Android application with Gradle before continuing. I’ll assume that from this point on, you have a functioning build.gradle script – with the exception of NDK integration of course – and that running ndk-build works as expected.

You’ll need to make sure you’re using a new version of Gradle and the Gradle plugin – Gradle v1.10 and Gradle Plugin for Android v0.8 should work fine.

Please refer to the build.gradle script in my project CorsixTH-Android for context.

Firstly, you’ll need to tell Gradle where to find your compiled libraries. I keep these in the libs folder in my project root. You do this by specifying the jniLibs source directory for your main source set (and any other you may have).

android {
  sourceSets {
    main { 
      manifest.srcFile 'AndroidManifest.xml'
      java.srcDirs = ['src/Java']
      resources.srcDirs = ['src']
      aidl.srcDirs = ['src']
      renderscript.srcDirs = ['src']
      res.srcDirs = ['res']
      assets.srcDirs = ['assets']
      jniLibs.srcDirs = ['libs']
    }
  }
}

Next, you need to add a new task to run ndk-build in order to build the libraries. We’ll call this task ndkBuild Add this to the bottom of your build.gradle script (outside of the android block):

import org.apache.tools.ant.taskdefs.condition.Os

task ndkBuild(type: Exec) {
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
      commandLine 'ndk-build.cmd', '-j'
    } else {
      commandLine 'ndk-build', '-j'
    }
}

Finally, we need to tell Gradle to kick this task off whenever we are compiling the app. Below the previous block, add this:

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn ndkBuild
}

From your terminal or command-line, run gradlew assemble to ensure everything is working as expected.

Travis CI

Travis CI is really quite simple to set up, and is totally free for open source projects. Read the Travis CI Getting Started page beforehand as it’ll tell you everything you need to know about how to link your GitHub project to Travis CI. If setup correctly, every time you push to your GitHub repository or a pull request is submitted, Travis CI will grab it and try to compile it. Any failures will be reported back to you via GitHub and by email. Travis CI gives you a fairly plain virtual machine instance on which to run your builds and although it often has many of our dependencies, they’re usually out of date so we’ll need to install our own.

Place the following file, named .travis.yml into the root of your git repository:

language: java
jdk: oraclejdk7

before_install:
  # required libs for android build tools
  - sudo apt-get update
  - sudo apt-get install -qq --force-yes libgd2-xpm ia32-libs ia32-libs-multiarch
  # for gradle output style
  - export TERM=dumb
  # newer version of gradle
  - wget http://services.gradle.org/distributions/gradle-1.10-bin.zip
  - unzip -qq gradle-1.10-bin.zip
  - export GRADLE_HOME=$PWD/gradle-1.10
  - export PATH=$GRADLE_HOME/bin:$PATH
  - chmod +x gradlew
  # newest Android SDK 22.3
  - wget http://dl.google.com/android/android-sdk_r22.3-linux.tgz
  - tar -zxf android-sdk_r22.3-linux.tgz
  - export ANDROID_HOME=`pwd`/android-sdk-linux
  - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
  # newest Android NDK
  - if [ `uname -m` = x86_64]; then wget http://dl.google.com/android/ndk/android-ndk-r9c-linux-x86_64.tar.bz2 -O ndk.tgz; else wget http://dl.google.com/android/ndk/android-ndk-r9c-linux-x86.tar.bz2 -O ndk.tgz; fi
  - tar -xf ndk.tgz
  - export ANDROID_NDK_HOME=`pwd`/android-ndk-r9c
  - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${ANDROID_NDK_HOME}
  # manually set sdk.dir variable, according to local paths
  - echo "sdk.dir=$ANDROID_HOME" > local.properties
  - echo yes | android update sdk -a -t tools,platform-tools,extra-android-support,extra-android-m2repository,android-19,build-tools-19.0.1,extra-google-google_play_services,extra-google-m2repository --force --no-ui

This should work largely unmodified, however you will have to make a couple of small tweaks if you use a newer version of the Android SDK, Android NDK, Gradle or the Android build tools. Change gradle-1.10 to whatever version of Gradle you are using. Change android-sdk_r22.3 to your SDK version (you can find the latest here). Change android-ndk-r9c to your NDK version (you can find the latest here).

The last line in the script tells the Android SDK to download the requirements for your build. You should make sure that you have the right SDK platform for whatever API version you are targeting and that the build-tools version is the same as referenced in your build.gradle file. A full list of all the Android SDK packages available as of 25/1/2014 can be found here.