Android AAR包含本地库

26

我正在寻找一种将本地库打包到AAR(Android Archive)中的方法,以便可以通过gradle脚本中的依赖声明来使用它。

通过本地库,我指的是一组.cpp文件或编译好的静态库和一组头文件。因此,我是指应用程序本身将从本机代码而不是Java中调用库。换句话说,这个库需要编译应用程序的本机代码,以便能够轻松管理本机代码的依赖项。

这种方式是否可行?

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


我发现这里https://github.com/googlesamples/android-ndk/tree/master/hello-libs可能有一些有趣的信息,但我仍然不知道如何将它们全部打包到AAR中。 - igagis
你可以看一下 https://github.com/realm/realm-java - 这是一个相当复杂的构建系统。你可以使用 Gradle 插件来包含 Realm。 - geisshirt
https://github.com/googlesamples/android-ndk/issues/261 - igagis
请在 GitHub 上尝试使用 android-fat-aar Gradle 脚本。 - Alex Cohn
@AlexCohn 谢谢你指出这一点,但我认为我已经做了类似的事情,请看下面我的回答。 - igagis
显示剩余3条评论
4个回答

6

= 更新2020-06-20 =

现在,有一个很好的插件nice plugin来解决这个问题,它工作得非常好。感谢作者和@Paulo Costa的指导。

=已过时=

发现了以下方法来解决这个问题:

使用Android Experimental Gradle插件版本0.9.1。 将库头文件和静态库放入.aar中。 头文件放在ndkLibs/include,静态库放在ndkLibs/<arch>,每种架构都有一个目录。 然后,在应用程序或依赖于这个打包库的另一个库中,我们只需要将AAR中的ndkLibs目录提取到项目中的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')

Google已经停止支持实验性Gradle插件,因此这种方法现在已经过时了。但也许可以通过标准插件实现类似的功能。 - igagis
很不幸,这个预制件似乎也已经停止了公共开发。三年过去了,我仍然没有看到像openssl + curl这样的最基本的软件被打包成预制件,而且插件还缺少一个非常重要的功能:从已编译的so库创建aar文件。 - undefined

2
手动修改gradle脚本可以实现目的,但是非常繁琐且容易出错。最近我发现了一个插件,它可以将头文件神奇地打包到AAR文件中,并在添加依赖时提取它们并设置构建脚本:https://github.com/howardpang/androidNativeBundle 关于可重用库:
  • Add the export plugin:

    apply plugin: 'com.ydq.android.gradle.native-aar.export'
    
  • Define where the header files are:

    nativeBundleExport {
        headerDir = "${project.projectDir}/src/main/jni/include"
    }
    

使用它的模块:

  • Add the import plugin:

    apply plugin: 'com.ydq.android.gradle.native-aar.import'
    
  • add include ${ANDROID_GRADLE_NATIVE_BUNDLE_PLUGIN_MK} to each module that depends on it in your 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)
    

有没有办法在基于CMake的Android构建中使用它? - igagis
1
AndroidNativeBundle支持它,但我还没有测试过。 - Paulo Costa
我正在考虑尝试AndroidNativeBundle,只是想问一下你为什么不喜欢它,以至于最终写了自己的插件?它有什么缺陷吗? - igagis
@igagis,你最终尝试使用这个插件了吗?似乎需要开发人员在上传和消费两端都使用它,这听起来并不理想。 - Ivan Garza
@IvanGarza 是的,我现在正在使用插件来管理所有我的库。是的,它必须在两侧都使用。 - igagis
显示剩余2条评论

0
从这个链接来看,似乎不可能。我在下面粘贴了内容: AAR文件的解剖 AAR文件的文件扩展名是.aar,Maven工件类型也应该是aar。文件本身是一个zip文件,包含以下必需条目:
  • /AndroidManifest.xml
  • /classes.jar
  • /res/
  • /R.txt

此外,AAR文件可能包含以下一个或多个可选条目:

  • /assets/
  • /libs/name.jar
  • /jni/abi_name/name.so(其中abi_name是Android支持的ABI之一)
  • /proguard.txt
  • /lint.jar

如上所述,强制条目包括一个jar文件。但是,您可以尝试通过手动删除jar文件并解压缩aar文件,然后再次压缩成zip文件来进行尝试。不过我不确定它是否会起作用。


1
很明显没有官方的方法来做到这一点,我在这里从开发者那里得到了直接的答案 https://github.com/googlesamples/android-ndk/issues/261,问题的意图是找到可能的好解决方法。 - igagis
你到底如何将可选项添加到 AAR 中呢?更具体地说,是 libsjni - Ivan Garza

0

虽然我个人没有尝试过,但我在这里找到了一些步骤here:

可能间接地[曾经尝试过共享库],但我个人认为这不值得:

  • 首先构建您的库以生成静态库和一个aar[model.library并未将*.a放入libs目录中]
  • 解压缩aar文件,并将*.a放入libs文件夹中
  • 找到头文件的位置
  • 将其重新打包成aar文件并将其作为依赖库添加到您的应用程序中,因此它将被提取到exploded-aar文件夹中;然后彩色图片出现了。
  • 将中间的explodeded aar目录添加到包含路径中 我认为这太hacky了,而且可能不是将这些强加给您的客户的好主意。

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


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接