Over a million developers have joined DZone.

Android Gradle: Add Native .so Dependencies

Maven dependencies convention allows you to specify the type of ABI and the format of the library you want to resolve using classifier.

· Mobile Zone

Background 

A few months ago, I wrote a Key-Value database for Android called SnappyDB based on Google’s LevelDB.  Since it uses native C++ code, the generated bundle contains (.so) binaries libs, along with Jars. 

Distribution via Maven repo is not a problem (As soon as you pass the hassle of the publishing process:), maven-android-plugican help you include the shared libs. Maven dependencies convention allows you to specify the type of ABI (different CPU architectures) & the format of the library (obviously .so in our case) you want to resolve using classifier: 

Ex: resolving ARM shared lib for SnappyDB:

<dependency>
  <groupId>com.snappydb</groupId>
  <artifactId>snappydb-native</artifactId>
  <version>0.2.0</version>
  <classifier>armeabi</classifier>
  <type>so</type>
</dependency>

This approach works fine if you use Maven & Eclipse ADT as a build system, until you succumbed to Gradle’s siren call! 

Android Studio & Gradle 

Android Gradle plugin, handle gracefully all Jars dependencies by using maven repos (among others …) 

ex: declaring a dependency inside build.gradle

dependencies {
     classpath 'commons-io:commons-io:2.4'
}

but it struggles when it comes to native dependencies, as compared with Maven, you can’t¹ write something like this:

dependencies {
       classpath 'com.snappydb:snappydb-native:2.+:arm-v7a'
}

This is due to the fact that the NDK support is still a work in progress with Android plugin. (as with Android Studio).

 ¹ actually, technically speaking you can, but Gradle will just ignore these native file since it doesn’t know what to do with them.

jniLibs to the rescue!

In their 0.7.2  release of the Android plugin, Google introduced a new folder ‘jniLibs‘ to the source sets. This means, that you can now add your prebuilt .so files to this folder, and Android plugin will take care of packaging those native libraries inside your APK.

.
├── AndroidManifest.xml
└── jniLibs
    ├── armeabi
    │   └── libsnappydb-native.so
    ├── armeabi-v7a
    │   └── libsnappydb-native.so
    ├── mips
    │   └── libsnappydb-native.so
    └── x86
        └── libsnappydb-native.so

This feature is great, but the developer still need to download & copy his prebuilt .so files manually, which isn’t great especially if you use a Continuous Integration server like Jenkins or Travis for instance. 

A lot of hacks & workarounds  emerged to try to sort this out, but a lot of them are really verbose & require the user to download manually his native dependencies. 

So, you get the picture. There has to be a better way.

Meet android-native-dependencies

android-native-dependenciesis a Gradle plugin I wrote to automate the process of resolving & downloading & copying the native dependencies into jniLibs folder, so Android plugin can include them automatically in your APK build. 

The plugin uses the same repository declared to resolve regular dependencies (jar) here is an example:

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:0.10.+'
    classpath 'com.nabilhachicha:android-native-dependencies:0.1'
  }
}

apply plugin: 'android'
apply plugin: 'android-native-dependencies'

native_dependencies {
    artifact 'com.snappydb:snappydb-native:0.2+:armeabi'
    artifact 'com.snappydb:snappydb-native:0.2+:x86'
}

dependencies {
    //regular Jar dependencies ...
}

Convention

The artifact DSL follows the naming convention for Maven artifacts. thus, you can use one of the following syntax: 

  • abbreviated group:name:version[:classifier]
//adding x86 classifier will resolve only intel's (.so) lib
native_dependencies {
    artifact 'com.snappydb:snappydb-native:0.2+:x86'
}

//omit the classifier will resolve all supported architectures
native_dependencies {
    artifact 'com.snappydb:snappydb-native:0.2+'
}
  • map-style notation
//adding x86 classifier will resolve only intel's (.so) lib
native_dependencies {
    artifact group: 'com.snappydb', name: 'snappydb-native', version: '0.2+', classifier: 'x86'
}

//omit the classifier will resolve all supported architectures
native_dependencies {
    artifact group: 'com.snappydb', name: 'snappydb-native', version: '0.2+'
}

In both notations, classifier is optional. this means that when omitted, the plugin try to resolve the artifacts for all architectures: armeabi, armeabi-v7a, x86 and mips

Conclusion

Until we get a full support for NDK in Android Gradle plugin, using android-native-dependencies can help you build your CI & automate repetitive task with native dependencies. Please try it and send your feedback to @nabilhachicha . 

Another great Gradle plugin I recommend is the android-sdk-manager  by (Jake Wharton) who helps downloads and manages your Android SDK.

Topics:
java ,mobile ,android ,plugin ,libraries ,native ,gradle ,arm ,.so

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}