【问题标题】:Android Gradle Adding static libraryAndroid Gradle 添加静态库
【发布时间】:2015-12-04 22:23:28
【问题描述】:

在旧的传统 android ndk 中,我们将在 Android.mk 文件中指定要链接的静态库。

Android.mk

PLATFORM_PREFIX := /opt/android-ext/
LOCAL_PATH := $(PLATFORM_PREFIX)/lib
include $(CLEAR_VARS)
LOCAL_MODULE := library
LOCAL_SRC_FILES := library.a
include $(PREBUILT_STATIC_LIBRARY)

LOCAL_STATIC_LIBRARIES := android_native_app_glue library

这是我的问题

在切换到 NDK 的 Gradle 实验插件时,我有点困惑。分享您对如何在 App build.gradle 文件中链接静态库的想法。

我关注了最新的gradle实验插件文档given here.

【问题讨论】:

    标签: android gradle android-ndk static-libraries


    【解决方案1】:

    看看这个sample

    1. 告诉编译器头文件在哪里(android.ndk{}):

      CFlags += "-I${file("path/to/headers")}".toString() cppFlags += CFlags

    2. 告诉链接器 .a 文件在哪里(在 android.ndk{} 中或在哪里 定义风味 - 确保添加 abiFilter - 例如 abiFilters += "armeabi-v7")

      ldFlags += "-L${file(path/to/library.a)}".toString() ldLibs += ["nameOfLibrary"]

      注意库名按照约定是后面的字符串 .a 文件名中的“lib”。例如,对于名为 libNative.a 的文件 您应该将 ldLibs += ["native"] 添加到 gradle。

    3. 创建一个新模块并使用apply plugin: 'java' 应用java 插件。在 build.gradle 中编写必要的代码来获取 .a 文件并将其放置在适当的目录中(您将从使用它的模块中获取它)。不要忘记使用库在模块中添加依赖项(dependencies{} 中的compile project(':libraryModule'))并将其包含在 settings.gradle 文件中的项目中,使用 include ':libraryModule'。如果您想将模块放置在您指定的文件夹中(例如当前您的 Android.mk 文件所在的位置),只需添加 project(':libraryModule').projectDir = new File(settingsDir, 'path/to/module')

    应该可以的。

    【讨论】:

    • 链接已更新。但是,该示例似乎已从 git 中的 Google 示例存储库中删除。 NDK 集成在过去几个月中一直在更新,现在可能有比示例中的方法更好的方法。
    • 它不适用于 android 2.2。错误消息:在 com.android.build.gradle.AppExtension 类型的对象上找不到参数 [blah] 的 ndk()。
    【解决方案2】:

    以上答案解决了 gradle 之前的 NDK 集成不足。这个答案说明了与 NDK 的新 gradle 集成。

    看看这个针对 gradle 2.9 和 android 插件 0.6.0-alpha1 编写的提议 sample。与提出问题的方式相反,此答案包含一个单独的库项目。可以探索此功能以允许 gradle 在应用程序项目使用该库之前构建该库。其他答案依赖于库已经构建的假设。

    :secondlib com.android.model.application 构建 libsecondlib.so(使用 System.loadLibrary("secondlib") 在 Java 代码中加载。名称 'secondlib' 的名字很糟糕。我喜欢将其视为 .so “包装器”用于链接以供应用使用的所有其他本机库。

    该共享库与由 :firstlib com.android.model.native 构建的 firstlib.a 静态链接。

    根据exportedHeaders 子句,标头从 :firstlib 导出到任何依赖项目(本示例中为:secondlib)。这样,依赖项目就知道如何链接到 .so/.a。这替换了先前答案中的 CFlags+="-I/path/to/headers" 语法。

    :secondlib 根据以下子句静态链接到 :firstlib:

    android.sources {
        main {
             jni {
                 dependencies {
                     project ":firstlib" buildType "debug" linkage "static"
                 }
             }
             // TODO(proppy): show jniLibs dependencies on .so
        }
    }
    

    如评论所示,示例不完整。完成语法显示在experimental android plugin documentation 的“NDK 依赖项”部分。在该文档中,如何静态链接而不是动态链接的语法应该很清楚。

    目前存在一些缺点(例如,上面显示的依赖项的 buildType 默认为“debug”,而不是当前正在构建的 buildType)。

    编辑:这是从我的一个项目中提取的 app/build.gradle 中新依赖语法的一个正在进行的示例:

      android.sources {
        main {
          jni {
            //for exportedHeaders
            dependencies { project ":libfoo" linkage "shared" }
          }
          jniLibs {
            //Where the swig wrapped library .so is. I use swig to create code to interface with libfoo.so within :app
            source { srcDirs 'libs' }
            //for file in $(model.repositories.libs.libfoo)
            dependencies { library "libfoo" }
          }
        }
      }
    
      repositories {
        libs(PrebuiltLibraries) {
          libevdev {
            //headers already available from our libfoo project via exportedHeaders
            //headers.srcDir "../libfoo/src/main/jni/"
            binaries.withType(SharedLibraryBinary) {
              sharedLibraryFile = file("../libfoo/build/intermediates/binaries/debug/lib/${targetPlatform.getName()}/libfoo.so")
            }
          }
        }
      }
    

    【讨论】:

    • 它不工作。我在添加 Sourcets 时遇到问题
    • @Sankar sourceSets?如果您阅读错误消息,它不再是模型语法的一部分(他们改变了一些类似的东西)。 'sourceSets{ main{ jniLibs{ srcDirs.add('jniLibs') }}}' 最初与库的 build.gradle 一起使用,以便将 .so 文件复制到应用程序项目中。据我所知,功能已移至应用程序的 build.gradle,如上面链接中的依赖项部分所示。我在答案中添加了该示例实现,并稍微澄清了答案,因为它假定您有一个单独的 gradle 项目来构建该静态库。
    • @Sankar 我也很抱歉,我写的答案是假设动态链接而不是静态链接。在我的方法中,应用程序项目包含库的接口代码,这个接口代码变成了一个名为“lib$(model.android.ndk.moduleName).so”的.so。该 .so 可以与从库项目构建的库代码静态链接。通过这种方式,我相信您会从静态库中删除未使用的代码(从而最小化应用程序大小)。有时间我会尝试将示例代码更改为静态链接(应该是简单的更改),并进一步制作更完整的示例。
    【解决方案3】:

    从概念上讲,apk 是清单、本机文件和类库的 zip。 因此,如果您将静态库复制到输出,它将起作用。 因此,在 gradle 中,您应该使用复制任务并将这些库作为输出的一部分。 我只是在下面使用了我的so文件并且它有效。

    task copyNativeLibs2h(type: Copy) {
        from(new File(getProjectDir(), 'libs')) { include '**/*.so' }
        into new File(buildDir, 'native-libs')
    }
    tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn copyNativeLibs2h }
    

    【讨论】:

    • 请让我知道我们必须在哪里添加此代码?
    【解决方案4】:

    您无法使用 gradle 构建静态库,即使使用实验性插件也是如此。您可以使用预构建的库,或者使用ndk-build 构建它并使用 gradle 插件将其链接到共享对象。

    这是一个例子:

    假设我们有这样的目录结构:

    • 项目(build.gradle、gradle.properties 等)
      • jni_static(Application.mk、Android.mk、静态库的 cpp 文件)
      • 应用程序(build.gradle、src/main 等)
        • jni_shared(共享库的 cpp 文件)

    以下是 project/app/build.gradle 的相关部分:

    // look for NDK directory
    
    import org.apache.tools.ant.taskdefs.condition.Os
    Properties properties = new Properties()
    properties.load(project.rootProject.file('local.properties').newDataInputStream())
    def ndkBuild = properties.getProperty('ndk.dir') + '/ndk-build'
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        ndkBuild += '.cmd'
    }
    
    apply plugin: 'com.android.model.application'
    

    … 依赖关系、模型 { compileOptions 等。

    // static lib is built with ndk-build
    
    def LOCAL_MODULE = "static"
    def appAbi = "armeabi-v7a"
    def ndkOut = "build/intermediates/$LOCAL_MODULE"
    def staticLibPath = "$ndkOut/local/$appAbi/lib${LOCAL_MODULE}.a"
    
    // To guarantee that the intermediates shared library is always refreshed,
    // we delete it in gradle task rmSO.
    
    task rmSO(type: Delete) {
        delete 'build/intermediates/binaries/debug/lib/armeabi-v7a', 'libshared.so'
        delete 'build/intermediates/binaries/debug/obj/armeabi-v7a', 'libshared.so'
    }
    
    // in file jni/Android.mk there is a section for LOCAL_MODULE=static
    // which builds the static library
    
    task buildStaticLib(type: Exec, description: 'Compile Static lib via NDK') {
        commandLine "$ndkBuild", "$staticLibPath", "NDK_APPLICATION_MK=../jni_static/Application.mk",
                "NDK_PROJECT_PATH=../jni_static", "NDK_OUT=$ndkOut"
        dependsOn rmSO
    }
    
    task cleanNative(type: Exec, description: 'Clean JNI object files') {
        commandLine "$ndkBuild", "clean", "NDK_APPLICATION_MK=../jni_static/Application.mk",
                "NDK_PROJECT_PATH=../jni_static", "NDK_OUT=$ndkOut"
    }
    clean.dependsOn cleanNative
    
    tasks.all {
        task ->
    
    // link of the shared library depends on build of the static lib
            if (task.name.startsWith('link')) {
                task.dependsOn buildStaticLib
            }
    
    // before build, make sure the intermediate so is not stuck there
            if (task.name.startsWith('package')) {
                task.dependsOn rmSO
            }
    }
    
    // build the wrapper shared lib around the static lib using the experimental plugin
    
    model {
        android.ndk {
            moduleName = "shared"
            cppFlags += "-std=c++11"
            ldFlags += "$staticLibPath".toString()
            ldLibs += "log"
    
            stl = "gnustl_static"
            abiFilters += "$appAbi".toString()
        }
    
        android.sources {
            main.jni.source {
                srcDirs = ["jni_shared"]
            }
        }
    }
    

    重要:共享库的源代码应该在一个单独的目录中,这样静态库的源代码不在它里面或者下面。这是因为实验插件无法排除srcDirs下的一些文件。

    【讨论】:

      猜你喜欢
      • 2011-12-01
      • 2015-12-15
      • 1970-01-01
      • 2015-03-06
      • 1970-01-01
      • 1970-01-01
      • 2017-11-23
      • 2013-10-29
      • 1970-01-01
      相关资源
      最近更新 更多