安卓Gradle添加静态库

15

在旧传统的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

这里是我的问题

当切换到用Gradle实验性插件构建NDK时,我有些困惑。请分享一下在App build.gradle文件中如何链接静态库的想法。

我已经按照此处给出的最新Gradle实验性插件文档进行了操作。

4个回答

10
请查看此示例
  1. 告诉编译器头文件在哪里(在 android.ndk{} 中):

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

  2. 告诉链接器 .a 文件在哪里(在 android.ndk{} 或定义flavors的地方 - 确保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')

这样应该就可以了。


链接已更新。然而,样例似乎已从Google的git示例存储库中删除。NDK集成在过去几个月中已经更新,现在可能有比样例中更好的方法。 - Nedko
它在Android 2.2上无法工作。错误信息:在类型为com.android.build.gradle.AppExtension的对象上找不到ndk()参数[blah]。 - r0n9

3
上述答案是解决gradle之前不足的NDK集成的方法。这个答案说明了新的gradle集成NDK的方法。
请查看针对gradle 2.9和android插件0.6.0-alpha1编写的示例。与问题提出的方式相反,此答案包含一个用于库的独立项目。可以探索此功能,以允许gradle在应用程序项目使用库之前构建该库。其他答案依赖于已经构建了库的假设。
:secondlib com.android.model.application构建libsecondlib.so(在Java代码中使用System.loadLibrary("secondlib")加载)。名称'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
    }
}

如评论所示,该示例不完整。完成语法在实验性Android插件文档的“NDK Dependencies”部分中显示。在该文档中,静态链接而非动态链接的语法应该很清楚。
目前存在一些缺点(例如,上面显示的依赖项的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 ganesh
@Sankar sourceSets?如果你读错误信息,它已经不再是模型语法的一部分了(他们改变了一些类似的东西)。'sourceSets{ main{ jniLibs{ srcDirs.add('jniLibs') }}}' 最初与库的 build.gradle 一起使用,以导致 .so 文件被复制到应用程序项目中。据我所知,该功能已移至应用程序的 build.gradle,如上面链接中的依赖项部分所示。我在我的答案中添加了示例实现,并澄清了答案,假定您有一个单独的 gradle 项目来构建该静态库。 - Andrew Smart
1
@Sankar 同时我也要道歉,我之前写的回答是以动态链接为前提的,而不是静态链接。 在我的方法中,应用程序项目包含了与库相关的接口代码,这些接口代码将成为一个名为“lib$(model.android.ndk.moduleName).so”的.so文件。该文件可以与从库项目构建的库代码进行静态链接。我相信通过这种方式,您可以剪去静态库中未使用的代码(从而使应用大小最小化)。当我有时间时,我会尝试更改我的示例代码以进行静态链接(这应该是简单的更改),并制作一个更完整的示例。 - Andrew Smart

1

从概念上讲,apk是清单、本地文件和类库的压缩包。 如果将静态库复制到输出目录,则应该可以正常工作。 因此,在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 }

请告诉我需要在哪里添加这段代码? - Chander Shakher Ghorela - Guru

0

使用gradle无法构建静态库,即使使用实验性插件也不行。您可以使用预编译的库,或者使用ndk-build进行构建,并使用gradle插件将其链接到共享对象中。

这里是一个示例:

假设我们有以下目录结构:

  • project (build.gradle, gradle.properties, 等等)
    • jni_static (Application.mk, Android.mk, 用于静态库的cpp文件)
    • app (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'

...依赖项,模型{编译选项等。

// 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下的某些文件。


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