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.
Join the DZone community and get the full member experience.
Join For FreeBackground
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-plugin can 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).
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.
Opinions expressed by DZone contributors are their own.
Comments