原生库的 Android AAR 包

Posted

技术标签:

【中文标题】原生库的 Android AAR 包【英文标题】:Android AAR package for native library 【发布时间】:2016-05-10 12:15:34 【问题描述】:

我正在寻找一种将本机库打包到 AAR 包中的方法,因此可以通过 gradle 脚本中的依赖项声明来使用它。

本地库是指一组 .cpp 文件或已编译的静态库和一组头文件。所以,我的意思是应用程序本身将从本机代码调用库,而不是从 Java。换句话说,该库需要编译应用程序的本机代码。这样就可以轻松管理原生代码的依赖关系。

有可能吗?

到目前为止,我只能找到很多关于如何使用 .so 文件及其 Java 接口制作 JNI 本机库的 AAR 的问题/示例,因此该库只是具有本机实现的 Java 库,但这不是什么我需要。

【问题讨论】:

我发现这里@9​​87654321@ 可能是一些有趣的信息,但我仍然不知道如何将其全部打包到 AAR 你可以看看github.com/realm/realm-java - 这是一个相当复杂的构建系统。您可以使用 Gradle 插件包含 Realm。 github.com/googlesamples/android-ndk/issues/261 在 GitHub 上尝试 android-fat-aar gradle 脚本 @AlexCohn 这对解决我的问题有何帮助?我看了看,它似乎根本不相关。 【参考方案1】:

= 2020-06-20 更新 =

现在,有一个 nice plugin 用于此,效果很好。 感谢它的作者和@Paulo Costa 的指点。

= 已过时 =

找到了以下解决问题的方法:

使用 Android Experimental Gradle 插件版本 0.9.1。 这个想法是将库头和静态库放入.aar。 对于每个架构,标头都放在ndkLibs/include 和静态库到ndkLibs/<arch>。然后,在应用程序或依赖于此打包库的另一个库中,我们只需将 ndkLibs 目录从 AAR 提取到项目中的 build 目录。请参阅下面的示例 gradle 文件。

带有 cmets 的库的 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')

【讨论】:

google 已经放弃了实验性的 gradle 插件,所以这种方法现在有点死了。但也许使用标准插件也可以实现类似的效果。【参考方案2】:

手动破解 gradle 脚本是可行的,但很痛苦且容易出错。

我最近发现了一个插件,它可以神奇地将标头捆绑到 AAR 文件中并提取它们并在添加依赖项时设置构建脚本:https://github.com/howardpang/androidNativeBundle

关于可重用库:

添加导出插件:

apply plugin: 'com.ydq.android.gradle.native-aar.export'

定义头文件的位置:

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)

【讨论】:

我也写了自己的替代插件:github.com/paulo-raca/ndkLibraries 有没有办法将它与基于 CMake 的 android 构建一起使用? AndroidNativeBundle 支持它,但我没有测试过。 NdkLibraries 可能在导出时可以工作,但没有导入代码 -- 欢迎 PRs 我正在考虑尝试AndroidNativeBundle,只是想问你为什么不喜欢它,所以你最终编写了自己的插件?有没有什么破绽?【参考方案3】:

从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 文件。我不确定它是否会起作用。

【讨论】:

很明显没有官方的方法,我在这里得到了开发者的直接回答github.com/googlesamples/android-ndk/issues/261,这个问题的目的是找到可能的好的解决方法 您如何将可选条目包含在 AAR 中?更具体地说,libsjni【参考方案4】:

虽然我没有亲自尝试过,但我发现了很多步骤here:

可能是间接的[前段时间尝试使用共享库],我个人认为不值得:

首先构建你的库以生成一个静态库和一个 aar [model.library 不会将 *.a 放入 libs 目录] 解压你的aar,把*.a放到libs文件夹中 为您的头文件找到一个位置 在你的app里面把它压缩回aar,把aar作为你的依赖库,这样它就会被解压到exploded-aar文件夹中;然后出现彩色图片。 将中间展开的 aar 目录添加到包含路径中 我认为这太老套了,将这些强加给您的客户可能不是一个好主意。

与上述黑客相比,直接分发 lib 和头文件的传统方式仍然更好。 对于构建库,cmake 方式要好得多,在 master-cmake 分支中检查 hello-libs,希望这会有所帮助

【讨论】:

是的,那个 github 问题是我提交的,我只是在寻找更好的解决方法,不需要用户手动输入包含路径

以上是关于原生库的 Android AAR 包的主要内容,如果未能解决你的问题,请参考以下文章

Flutter混合开发之FlutterFragment

怎么改变android studio中库的aar文件的输出目录

当它具有也是 aar 库的依赖项时,如何为 android 库生成 javadoc?

原生Android工程接入Flutter aar

原生Android工程接入Flutter aar

Android - 当 minifyEnabled true 导致 java.lang.ExceptionInInitializerError 时,带有混淆 AAR 库的应用程序崩溃