Android 本机库的 AAR 包
Android AAR package for native library
我正在寻找一种将本机库打包到 AAR 包中的方法,因此可以通过 gradle 脚本中的依赖项声明来使用它。
本机库是指一组 .cpp 文件或已编译的静态库和一组头文件。所以,我的意思是应用程序本身将从本机代码而不是 Java 调用库。换句话说,库需要编译应用程序的本机代码。这样就可以轻松管理本机代码的依赖关系。
有可能吗?
到目前为止,我只能找到很多 questions/examples 如何使用 .so 文件及其 Java 接口制作 JNI 本机库的 AAR,所以该库只是一个 Java 带有本机实现的库,但这不是我需要的。
从这个Link看来,不太可能。我在下面粘贴内容:
AAR 文件剖析
AAR 文件的文件扩展名是.aar,Maven 工件类型也应该是aar。该文件本身是一个包含以下必需条目的 zip 文件:
- /AndroidManifest.xml
- /classes.jar
- /res/
- /R.txt
此外,AAR 文件可能包含以下一个或多个可选条目:
- /资产/
- /libs/name.jar
- /jni/abi_name/name.so(其中 abi_name 是 Android 支持的 ABI 之一)
- /proguard.txt
- /lint.jar
如上所说,必填项包括一个jar。但是,您可以尝试通过解压缩 aar 并再次压缩来手动删除 jar 文件。不过我不确定它是否有效。
虽然我没有亲自尝试过,但我发现了一些步骤here:
可能是间接的[前段时间用共享库试过一次],我个人认为不值得:
- 首先构建你的库以生成一个静态库,然后一个aar
[model.library 不会将 *.a 放入 libs 目录]
- 解压你的aar,并将*.a放入libs文件夹
- 为您的头文件找一个地方
- 将其压缩回应用程序中的 aar,将 aar 作为您的依赖库,因此它将被提取到 exploded-aar 文件夹中;然后出现彩色图片。
- 将中间展开的aar目录添加到include路径
我认为这太老套了,将这些强加给您的客户可能不是一个好主意。
与上述黑客攻击相比,直接分发 lib 和头文件的传统方式仍然更好。
对于构建库,cmake 方法要好得多,在 master-cmake 分支中检查 hello-libs,希望这有帮助
= 更新 2020-06-20 =
如今,有一个 nice plugin 可以做到这一点,效果很好。
感谢其作者和@Paulo Costa 对它的指点。
=已过时=
找到以下问题的 hacky 解决方案:
使用 Android 实验性 Gradle 插件版本 0.9.1。
这个想法是将库 headers 和静态库放入.aar。
对于每个体系结构,headers 被放入 ndkLibs/include
,静态库被放入 ndkLibs/<arch>
。然后,在应用程序或依赖于此打包库的另一个库中,我们只需将 ndkLibs
目录从 AAR 提取到项目中的 build
目录。请参阅下面的示例 gradle 文件。
库的 build.gradle
文件带有注释:
apply plugin: "com.android.model.library"
model {
android {
compileSdkVersion = 25
buildToolsVersion = '25.0.2'
defaultConfig {
minSdkVersion.apiLevel = 9
targetSdkVersion.apiLevel = 9
versionCode = 1
versionName = '1.0'
}
ndk {
platformVersion = 21
moduleName = "mylib"
toolchain = 'clang'
abiFilters.addAll(['armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64']) //this is default
ldLibs.addAll(['android', 'log'])
stl = 'c++_static'
cppFlags.add("-std=c++11")
cppFlags.add("-fexceptions")
cppFlags.add("-frtti")
//Add include path to be able to find headers from other AAR libraries
cppFlags.add("-I" + projectDir.getAbsolutePath() + "/build/ndkLibs/include")
}
//For each ABI add link-time library search path to be able to link against other AAR libraries
abis {
create("armeabi") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/armeabi")
}
create("armeabi-v7a") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/armeabi-v7a")
}
create("arm64-v8a") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/arm64-v8a")
}
create("x86") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/x86")
}
create("x86_64") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/x86_64")
}
create("mips") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/mips")
}
create("mips64") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/mips64")
}
}
}
//Configure this library source files
android.sources {
main {
jni {
//This does not affect AAR packaging
exportedHeaders {
srcDir "../../src/"
}
//This tells which source files to compile
source {
srcDirs '../../src'
}
}
}
}
}
//Custom Maven repository URLs to download AAR files from
repositories {
maven {
url 'https://dl.bintray.com/igagis/android/'
}
}
//Our custom AAR dependencies, those in turn are also packed to AAR using the same approach
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'io.github.igagis:libutki:+'
compile 'io.github.igagis:libsvgdom:+'
compile 'org.cairographics:cairo:+'
}
//===================================
//=== Extract NDK files from AARs ===
//This is to automatically extract ndkLibs directory from AAR to build directory before compiling any sources
task extractNDKLibs {
doLast {
configurations.compile.each {
def file = it.absoluteFile
copy {
from zipTree(file)
into "build/"
include "ndkLibs/**/*"
}
}
}
}
build.dependsOn('extractNDKLibs')
tasks.whenTaskAdded { task ->
if (task.name.startsWith('compile')) {
task.dependsOn('extractNDKLibs')
}
}
//=================================
//=== pack library files to aar ===
//This stuff re-packs the release AAR file adding headers and static libs to there, but removing all shared (.so) libs, as we don't need them. The resulting AAR is put to the project root directory and can be uploaded to Maven along with POM file (you need to write one by hand).
def aarName = name
task copyNdkLibsToAAR(type: Zip) {
baseName = aarName
version = "$(version)"
extension = 'aar.in'
destinationDir = file('..') //put resulting AAR file to upper level directory
from zipTree("build/outputs/aar/" + aarName + "-release.aar")
exclude('**/*.so') //do not include shared libraries into final AAR
from("../../src") {
exclude('makefile')
exclude('soname.txt')
exclude('**/*.cpp')
exclude('**/*.c')
into('ndkLibs/include')
}
from("build/intermediates/binaries/debug/lib"){
include('**/*.a')
into('ndkLibs')
}
}
build.finalizedBy('copyNdkLibsToAAR')
手动破解 gradle 脚本可行,但很痛苦并且 error-prone。
我最近发现了一个插件,它神奇地将 headers 捆绑到 AAR 文件中并提取它们并在添加依赖项时设置构建脚本:https://github.com/howardpang/androidNativeBundle
关于可重用库:
添加导出插件:
apply plugin: 'com.ydq.android.gradle.native-aar.export'
定义 header 文件的位置:
nativeBundleExport {
headerDir = "${project.projectDir}/src/main/jni/include"
}
在使用它的模块上:
添加导入插件:
apply plugin: 'com.ydq.android.gradle.native-aar.import'
将 include ${ANDROID_GRADLE_NATIVE_BUNDLE_PLUGIN_MK}
添加到 Android.mk
中依赖于它的每个模块:
include $(CLEAR_VARS)
LOCAL_SRC_FILES := myapp.cpp \
LOCAL_MODULE := myapp
LOCAL_LDLIBS += -llog
include ${ANDROID_GRADLE_NATIVE_BUNDLE_PLUGIN_MK}
include $(BUILD_SHARED_LIBRARY)
我正在寻找一种将本机库打包到 AAR 包中的方法,因此可以通过 gradle 脚本中的依赖项声明来使用它。
本机库是指一组 .cpp 文件或已编译的静态库和一组头文件。所以,我的意思是应用程序本身将从本机代码而不是 Java 调用库。换句话说,库需要编译应用程序的本机代码。这样就可以轻松管理本机代码的依赖关系。
有可能吗?
到目前为止,我只能找到很多 questions/examples 如何使用 .so 文件及其 Java 接口制作 JNI 本机库的 AAR,所以该库只是一个 Java 带有本机实现的库,但这不是我需要的。
从这个Link看来,不太可能。我在下面粘贴内容:
AAR 文件剖析
AAR 文件的文件扩展名是.aar,Maven 工件类型也应该是aar。该文件本身是一个包含以下必需条目的 zip 文件:
- /AndroidManifest.xml
- /classes.jar
- /res/
- /R.txt
此外,AAR 文件可能包含以下一个或多个可选条目:
- /资产/
- /libs/name.jar
- /jni/abi_name/name.so(其中 abi_name 是 Android 支持的 ABI 之一)
- /proguard.txt
- /lint.jar
如上所说,必填项包括一个jar。但是,您可以尝试通过解压缩 aar 并再次压缩来手动删除 jar 文件。不过我不确定它是否有效。
虽然我没有亲自尝试过,但我发现了一些步骤here:
可能是间接的[前段时间用共享库试过一次],我个人认为不值得:
- 首先构建你的库以生成一个静态库,然后一个aar [model.library 不会将 *.a 放入 libs 目录]
- 解压你的aar,并将*.a放入libs文件夹
- 为您的头文件找一个地方
- 将其压缩回应用程序中的 aar,将 aar 作为您的依赖库,因此它将被提取到 exploded-aar 文件夹中;然后出现彩色图片。
- 将中间展开的aar目录添加到include路径 我认为这太老套了,将这些强加给您的客户可能不是一个好主意。
与上述黑客攻击相比,直接分发 lib 和头文件的传统方式仍然更好。 对于构建库,cmake 方法要好得多,在 master-cmake 分支中检查 hello-libs,希望这有帮助
= 更新 2020-06-20 =
如今,有一个 nice plugin 可以做到这一点,效果很好。 感谢其作者和@Paulo Costa 对它的指点。
=已过时=
找到以下问题的 hacky 解决方案:
使用 Android 实验性 Gradle 插件版本 0.9.1。
这个想法是将库 headers 和静态库放入.aar。
对于每个体系结构,headers 被放入 ndkLibs/include
,静态库被放入 ndkLibs/<arch>
。然后,在应用程序或依赖于此打包库的另一个库中,我们只需将 ndkLibs
目录从 AAR 提取到项目中的 build
目录。请参阅下面的示例 gradle 文件。
库的 build.gradle
文件带有注释:
apply plugin: "com.android.model.library"
model {
android {
compileSdkVersion = 25
buildToolsVersion = '25.0.2'
defaultConfig {
minSdkVersion.apiLevel = 9
targetSdkVersion.apiLevel = 9
versionCode = 1
versionName = '1.0'
}
ndk {
platformVersion = 21
moduleName = "mylib"
toolchain = 'clang'
abiFilters.addAll(['armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64']) //this is default
ldLibs.addAll(['android', 'log'])
stl = 'c++_static'
cppFlags.add("-std=c++11")
cppFlags.add("-fexceptions")
cppFlags.add("-frtti")
//Add include path to be able to find headers from other AAR libraries
cppFlags.add("-I" + projectDir.getAbsolutePath() + "/build/ndkLibs/include")
}
//For each ABI add link-time library search path to be able to link against other AAR libraries
abis {
create("armeabi") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/armeabi")
}
create("armeabi-v7a") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/armeabi-v7a")
}
create("arm64-v8a") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/arm64-v8a")
}
create("x86") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/x86")
}
create("x86_64") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/x86_64")
}
create("mips") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/mips")
}
create("mips64") {
ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/mips64")
}
}
}
//Configure this library source files
android.sources {
main {
jni {
//This does not affect AAR packaging
exportedHeaders {
srcDir "../../src/"
}
//This tells which source files to compile
source {
srcDirs '../../src'
}
}
}
}
}
//Custom Maven repository URLs to download AAR files from
repositories {
maven {
url 'https://dl.bintray.com/igagis/android/'
}
}
//Our custom AAR dependencies, those in turn are also packed to AAR using the same approach
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'io.github.igagis:libutki:+'
compile 'io.github.igagis:libsvgdom:+'
compile 'org.cairographics:cairo:+'
}
//===================================
//=== Extract NDK files from AARs ===
//This is to automatically extract ndkLibs directory from AAR to build directory before compiling any sources
task extractNDKLibs {
doLast {
configurations.compile.each {
def file = it.absoluteFile
copy {
from zipTree(file)
into "build/"
include "ndkLibs/**/*"
}
}
}
}
build.dependsOn('extractNDKLibs')
tasks.whenTaskAdded { task ->
if (task.name.startsWith('compile')) {
task.dependsOn('extractNDKLibs')
}
}
//=================================
//=== pack library files to aar ===
//This stuff re-packs the release AAR file adding headers and static libs to there, but removing all shared (.so) libs, as we don't need them. The resulting AAR is put to the project root directory and can be uploaded to Maven along with POM file (you need to write one by hand).
def aarName = name
task copyNdkLibsToAAR(type: Zip) {
baseName = aarName
version = "$(version)"
extension = 'aar.in'
destinationDir = file('..') //put resulting AAR file to upper level directory
from zipTree("build/outputs/aar/" + aarName + "-release.aar")
exclude('**/*.so') //do not include shared libraries into final AAR
from("../../src") {
exclude('makefile')
exclude('soname.txt')
exclude('**/*.cpp')
exclude('**/*.c')
into('ndkLibs/include')
}
from("build/intermediates/binaries/debug/lib"){
include('**/*.a')
into('ndkLibs')
}
}
build.finalizedBy('copyNdkLibsToAAR')
手动破解 gradle 脚本可行,但很痛苦并且 error-prone。
我最近发现了一个插件,它神奇地将 headers 捆绑到 AAR 文件中并提取它们并在添加依赖项时设置构建脚本:https://github.com/howardpang/androidNativeBundle
关于可重用库:
添加导出插件:
apply plugin: 'com.ydq.android.gradle.native-aar.export'
定义 header 文件的位置:
nativeBundleExport { headerDir = "${project.projectDir}/src/main/jni/include" }
在使用它的模块上:
添加导入插件:
apply plugin: 'com.ydq.android.gradle.native-aar.import'
将
include ${ANDROID_GRADLE_NATIVE_BUNDLE_PLUGIN_MK}
添加到Android.mk
中依赖于它的每个模块:include $(CLEAR_VARS) LOCAL_SRC_FILES := myapp.cpp \ LOCAL_MODULE := myapp LOCAL_LDLIBS += -llog include ${ANDROID_GRADLE_NATIVE_BUNDLE_PLUGIN_MK} include $(BUILD_SHARED_LIBRARY)