如何在Android Gradle中添加原生so文件依赖

@author ASCE1885的 Github 简书 微博 CSDN
原文链接

背景

几个月前,基于Google的LevelDB,我为Android平台写了一个名为SnappyDB的键值数据库(NoSQL)。

由于它使用到了原生C++代码,因此最终生成的文件除了Jars包还有so文件。

通过Maven仓库发布我们的函数库没有什么问题(只要你通过繁琐的发布流程),maven-android-plugin可以帮助我们包含共享库。Maven依赖规则使得我们可以指定想要获取的ABI(不同的CPU架构)的类型以及函数库的格式(我们的是.so格式)。

例如,为SnappyDB获取ARM平台共享库:

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

如果你使用的是Maven+Eclipse ADT来构建你的Android应用,使用这个方法没有问题,但如果你将工程迁移到Android Studio+Gradle上,问题就来了。

Android Studio & Gradle

Android的Gradle插件能够很好的处理使用maven仓库的所有的jars依赖(以及其他)。

例如,在build.gradle文件中声明依赖:

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

但是当需要依赖原生函数库时就遇到问题了,类比于Maven,我们不再能够以下面这种方式来加载了:

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

这是因为Android Studio插件对于NDK的支持还做不到这一点。

jniLibs拯救了我们

在Android Studio的0.7.2版本的Android插件中,Google在工程的source sets中引入了一个新的目录‘jniLibs’。这意味着我们可以把预先编译好的.so文件拷贝到这个目录中,之后Android插件就会帮我们将这些原生函数库打包进APK中。

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

这个特性很强大,不过开发者还是需要下载预先编译好的.so文件,并手动拷贝到这个目录中,当我们使用类似Jenkins或者Travis的持续集成系统时就显得不完美了。

已经有很多hacks技巧和解决方法尝试解决这个问题,但很多实际上很繁琐同时要求用户手动下载并拷贝这些原生函数库依赖。

因此,我们需要有更好的解决方案。

android-native-dependencies的引入

android-native-dependencies是我写的一个自动处理查找&下载&拷贝原生函数库依赖到jniLibs目录的Android插件,这样在APK构建过程中可以自动包含这些函数库。

这个插件使用跟声明查找jar包一样的仓库,下面是一个例子:

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 ...
}

规约

DSL artifact遵循Maven artifacts的命名规则。因此,我们下面两种语法都可以使用:

  • 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+'
}
  • 映射风格
//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+'
}

在每种语法中,classifier都是可选的。这意味着,当忽略classifier时,插件将会尝试获取所有类型CPU架构(armeabi, armeabi-v7a, x86和mips)的artifacts。

总结

在Android Gradle插件完全支持NDK之前,使用android-native-dependencies可以帮助我们构建CI和自动化原生依赖的重复性的任务。

我同时推荐另一个很棒的由Jake Wharton写的Gradle插件:android-sdk-manager,它可以帮助你下载和管理Android SDK。