如何使用 Android Gradle 插件 0.7 配置 NDK

2024-03-23

新的 Android gradle 插件 (0.7) 似乎包含了对 NDK 的新支持,但在文档中几乎没有提到它(我找到的唯一参考是一个名为ndkSanAngeles https://android.googlesource.com/platform/tools/build/+/2e1f7810edd76d92cee8d3e06bc4dec0c288adea/tests/ndkSanAngeles/build.gradle).

看起来 gradle 正在寻找 NDK,我已将其包含在我的 PATH 中。但是,构建项目失败了

  • 什么地方出了错: 任务“:OGLTests:compileDefaultFlavorDebugNdk”执行失败。 NDK 未配置

如何在 gradle 中配置 NDK?

我当前的 build.gradle 如下所示:

task nativeLibsToJar(type: Zip, description: 'create a jar with native libs') {
    destinationDir file("$buildDir/native-libs")
    baseName 'native-libs'
    extension 'jar'
    from fileTree(dir: 'src/main/libs', include: '**/*.so')
    from fileTree(dir: 'src/main/libs', include: '**/gdb*')
    into 'lib/'
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn nativeLibsToJar
}

dependencies {
    compile fileTree(dir: "$buildDir/native-libs", include: '*.jar')
}

android {
    compileSdkVersion 19
    buildToolsVersion '19.0.0'

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 19
        versionCode 1
        versionName "0.1"

    }
    buildTypes {
        release {
            runProguard false
        }
        debug {
           // jniDebugBuild true
            runProguard false
            debuggable true
        }
    }
    productFlavors {
        defaultFlavor {
            proguardFile 'proguard-rules.txt'
        }
    }
}

Thanks.


查看 gradle 插件代码,我发现以下内容可以帮助我使用 NDK 和预构建的本机库:

简单来说预建本机库中的链接,只需将 ndk 部分添加到您的任务中即可。例如,我将其添加到下面的productFlavors 中。 abiFilter 是存储库的文件夹名称。abiFilters 意味着逗号分隔列表中的两个库都将添加到您的最终 APK 中(因此理论上您可以拥有“armeabi”、“armeabi-v7a”、“x86”和“ mips”全部集成在一个 APK 中,操作系统将在安装时选择支持的架构库):

productFlavors {
    arm {
        ndk {
            abiFilters "armeabi", "armeabi-v7a"
        }
    }
    x86 {
        ndk {
            abiFilter "x86"
        }
    }
}

在此示例中,arm 构建将创建一个包含 V5 和 V7A arm 库的 APK,而 x86 构建将创建一个仅包含 x86 库的 APK。这将在您的项目 jniLibs 目录中搜索本机库。 jniLibs 目录的结构应与旧的 jni 目录相同,即:

[project]/[app]/src/main/jniLibs/armeabi/libmyNative.so
[project]/[app]/src/main/jniLibs/armeabi-v7a/libmyNative.so
[project]/[app]/src/main/jniLibs/x86/libmyNative.so

然后你可以在Java中加载它,如下所示:

static
{
    loadLibrary("myNative");
}

现在,假设一个本机库依赖于另一个库。您必须(如果将最小 API 设置为 API 17 或更低)首先加载依赖库:

static
{
    loadLibrary("myDependency");
    loadLibrary("myNative");
}

您还可以将 ndk {} 部分放在 defaultConfig 或 buildType 中(例如调试或发布或您可能使用的其他任何内容)。例如:

buildTypes {
    debug {
        ndk {
            abiFilters "armeabi", "armeabi-v7a"
        }
    }
}

我所说的预构建是指您下载的第 3 方库或使用 NDK 工具链或您自己的 ARM 工具链(不是 ndk-build 脚本本身)构建的库。

在 API 18 中,他们修复了一个长期存在的架构问题,该问题阻止本机 lib 加载器“自动”加载依赖项,因为它不知道应用程序的 lib 目录(安全原因等)。在 API 18 及更高版本中,如果 myNative 依赖于上面的 myDependency,您只需调用 loadLibrary("myNative"),操作系统将处理加载 myDependency。不过,在运行 API 17 及以下版本的设备的市场渗透率达到您可以接受的较低水平之前,请不要依赖这一点。

明确地从源代码构建 NDK 库在当前版本的Android Studio中,您可以执行以下操作:

将 local.properties 中的 ndk.dir 值设置为指向 NDK 主目录,如前所述。有谁知道是否可以直接在 local.properties 中使用环境变量? :)

在你的 build.gradle 文件中,将类似的内容添加到你的任务中(同样,可以是 defaultConfig、debug、release、productFlavor 等):

ndk {
    moduleName "myNDKModule"
    stl "stlport_shared"
    ldLibs "log", "z", "m"
    cFlags "-I/some/include/path"
}

这是当前支持的类型(moduleName、stl、ldLibs 和 cFlags)的基本结构。我看了看,没有发现比这更多的东西。我认为 ldLibs 有一个问题,因为它会自动将“-l”添加到上面每个字段的前面。你可以通过说(我不得不)来欺骗它: ldLibs“log -lz -lm -Wl,-whole-archive -l /路径/到/someOtherLib -Wl,-no-whole-archive”

在这一行中,您只需标记第一个参数的末尾以添加不以 -l 开头的参数,这样您就可以暂时使用了。在上面的例子中,我将整个静态库链接到我的 NDK 模块中,以便在 Java 中使用。我已要求 google 开发人员添加其他功能,以允许此功能甚至能够将您自己的 Android.mk 文件合并到 NDK 构建过程中,但由于这是全新的,因此可能需要一段时间。

目前,无论您在 build.gradle 中放入什么内容,都会删除临时构建目录并每次重新创建它,因此除非您想下载并修改 gradle android 插件源代码(这会很有趣),否则会有一些“make due”像这样将你的东西复制到构建中。提供此 ndk 支持的 android gradle 脚本本质上会生成一个 Android.mk 文件,并在临时目录中使用 NDK 系统进行构建。

偏离了一会儿。 moduleName 应与项目中 jni 目录下的 c 或 cpp 文件匹配,例如:

[project]/[app]/src/main/jni/myNDKModule.cpp

如果要使用 C++ 的 stlport 库,则应将 stl 值设置为“stlport_shared”或“stlport_static”值。如果您不需要扩展的 C++ 支持,则可以省略 stl。请记住,Android 默认提供非常基本的 C++ 支持。对于其他支持的 C++ 库,请查看您下载的 NDK 中的 NDK 文档指南。请注意,通过在此处将其设置为 stlport_shared,gradle 会将 libstlport_shared.so lib 从 NDK 的 resources/cxx-stl/stlport/libs 目录复制到 APK 的 lib 目录。它还处理编译器中的包含路径(从技术上讲,gradle 并不执行所有这些操作,而是由 Android NDK 构建系统执行)。因此,不要将您自己的 stlport 副本放入 jniLibs 目录中。

最后,我认为 cFlags 非常明显。

您无法在 Mac OSX 上设置 ANDROID_NDK_HOME (见下文),但从我所做的一些研究来看,这可能仍然适用于其他操作系统。但它将被删除。

我想发表评论,但还没有声誉。丹尼斯,环境变量被完全忽略,而不仅仅是被覆盖。事实上,您没有获得任何环境变量。据我所知,Android Studio IDE 仅使用一些特定的环境变量创建自己的环境(检查 System.getenv() 并从 gradle 脚本中将其打印出来)。

我在这里将其写为错误,因为使用环境变量可以从 cmd 行构建良好:
https://code.google.com/p/android/issues/detail?id=65213 https://code.google.com/p/android/issues/detail?id=65213

但正如您所看到的,Google 决定根本不希望 IDE 使用环境变量;我仍然对这个决定持观望态度。必须更新 local.properties 以指向可以在我的 gradle 脚本中加载的绝对路径,这让我的生活很痛苦,我还没有弄清楚如何做到这一点(但还没有看起来那么难)。这意味着我要么强迫我的团队成员使用与我相同的路径,使用链接,让他们在每次拉取存储库时都输入它们,要么添加自动化脚本。我认为这是一个错误的决定,对于任何依赖环境变量的开发人员来说都会花费时间,环境变量在微观层面可能很小,但在宏观层面却很大。

groundloop,我相信 IDE 很快就会更新,能够将 NDK 文件夹路径添加到您的项目中,并且它将自动生成 local.properties 文件(如果他们没有想到的话,至少这是没有意义的)这)。

有关 Google 的更详细示例,以下是最新示例(搜索 jni 或 ndk):


使用 NDK 的跨平台胖 APK:

最后,使用 gradle 存在一个缺点,无法提供您自己的 Android.mk 文件,因此您只能将 3rd 方本机库从单一架构链接到您的 NDK。注意我说的是“链接”。您可以使用“abiFilters”命令在多个架构中构建 NDK 模块(上面的 moduleName),它们将被放置在您的应用程序中,以便可以在多个架构上使用相同的 APK。如果您需要链接自己的第 3 方库,或者甚至根据您的架构具有不同的 cFlags 值,则没有简单的方法。

我尝试了以下方法,一开始似乎有效,但后来我发现它只是通过将两个 ndk 部分中的所有内容附加在一起来构建 NDK(或者类似的东西,但它确实以某种方式构建了多个架构库):

android {
    compileSdkVersion 23
    buildToolsVersion '23.0.1'
    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 28
        versionName "3.0"
    }
    buildTypes {
        def commonLibs = " -lfoo -lbar -lwhatever"
        def armV7LibsDir = "/whatever/armv7a/libs"
        def armX86LibsDir = "/whatever/x86/libs"
        def armV7IncDir = "/whatever/armv7a/include"
        def x86IncDir = "/whatever/x86/include"
        debug {
            ndk {
                cFlags = "-I" + armV7IncDir
                moduleName "myNativeCPPModule"
                stl "stlport_shared"
                abiFilter "armeabi-v7a"
                ldLibs "log -L" + armV7LibsDir + commonLibs
            }
            ndk {
                cFlags = "-I" + armX86IncDir
                moduleName "myNativeCPPModule"
                stl "stlport_shared"
                abiFilter "x86"
                ldLibs "log -L" + armX86LibsDir + commonLibs
            }
        }
    }
}

经过一番痛苦的尝试,在干净的庄园中使用 gradle 和本机第 3 方库创建一个胖二进制文件,我终于得出结论,Google Play 对 APK 的内置多架构支持确实是最好的途径,所以创建每个架构都有单独的 APK。

因此,我创建了多个 buildType,没有产品风格,并添加了以下代码来生成每种类型的版本代码。

// This is somewhat nasty, but we need to put a "2" in front of all ARMEABI-V7A builds, a "3" in front of 64-bit ARM, etc.
// Google Play chooses the best APK based on version code, so if a device supports both X86 and
// ARM, it will choose the X86 APK (preferred because Inky ARM running on an X86 with Houdini ARM Emulator crashes in our case)
android.applicationVariants.all { variant ->
    if (variant.buildType.name.equals('release')) {
        variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debug')) {
        variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugArmV8a')) {
        variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseArmV8a')) {
        variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugMips')) {
        variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseMips')) {
        variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugMips64')) {
        variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseMips64')) {
        variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugX86')) {
        variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseX86')) {
        variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugX86_64')) {
        variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseX86_64')) {
        variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode
    }
}

现在您所要做的就是在 defaultConfig 对象中设置 versionCode 的值,就像您通常所做的那样,这会根据构建类型将其附加到特定于体系结构的版本字符串的末尾。所有版本的版本字符串都保持不变,但会改变代码以提供从 ARM 一直到 X86_64 的优先顺序。它有点黑客或硬编码,但它可以完成工作。请注意,这为您提供了最多 999 个版本,因此如果您需要更多版本,请将上面的数字乘以 10,不确定您可以为版本代码输入的最大值是多少。

就我而言,我们有一个相当复杂的构建系统。我们为 9 种架构构建 CPython,其中 3 种是 Android,然后构建一堆我们自己的库,并将它们全部链接到每个架构的单个库中。我们使用 ndk 命令行构建工具、automake 和 python 来构建所有内容,而不是 Android.mk 文件。然后,最终的库将链接到单个 JNI 接口 cpp 文件(上面称为 myNativeCPPModule)。一键点击按钮,一切就全部构建完毕,非常好的 Android Studio。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Android Gradle 插件 0.7 配置 NDK 的相关文章

随机推荐

  • 使用相同的 udp 套接字进行异步接收/发送

    我在 udp 服务器中使用相同的套接字 以便在某个端口上接收来自客户端的数据 然后在处理请求后使用 ip ud socket async send to 响应客户端 接收也是与 async receive from 异步完成的 套接字使用相
  • Knuth 计算机编程艺术 ex 1.1.8

    我无法理解 Knuth 在第 1 1 章练习 8 的说明中的含义 任务是制定一个有效的两个正整数的 gcd 算法m and n使用他的符号theta j phi j b j and a j 其中 theta 和 phi 是字符串 a and
  • 如何访问 Mercurial 进程内挂钩中的提交消息?

    我一直在努力 def debug hook ui repo kwargs changectx repo None ui status change desc s n changectx description return True 但它总
  • 通过 Emacs 启动时如何配置 SBCL 以使用更多 RAM?

    如何配置 SBCL 使其在使用 Emacs 中的 M x slime 启动时使用比默认值更多的内存 从我在网上看到的情况来看 答案似乎是调用 SBCL 传递参数 dynamic space size 由于我不直接调用 SBCL 因此我不知道
  • FPDF 和欧元符号的问题

    我花了几天时间筛选各种方法来鼓励 FPDF 渲染欧元符号 但没有成功 我有 currency iconv UTF 8 ISO 8859 1 TRANSLIT 结果是 iconv function iconv 检测到不完整的多字节字符 在输入
  • 在滚动视图上拖动视图:收到touchesBegan,但未收到touchesEnded 或touchesCancelled

    作为一名 iOS 编程新手 我正在努力解决iPhone 上的文字游戏 https github com afarber ios newbie tree master ScrollContent 应用程序结构是 scrollView gt c
  • 如何设置dropdownlist高度以及如何显示dropdownlist列表始终向下显示

    如何在 C 中设置下拉列表控件的高度 我尝试了这个 但它不起作用 cbo Attributes Add style height 50 而且 如何确保下拉列表的列表始终向下而不是向上 终于我明白了 cbo Height new Unit 2
  • Python - 读取 Emoji Unicode 字符

    我有一个 Python 2 7 程序 它从 SQLite 数据库读取 iOS 文本消息 文本消息是 unicode 字符串 在下面的短信中 u that u2019s U0001f63b 撇号表示为 u2019 但表情符号由 U0001f6
  • 具有 GIT 支持的 PL/SQL IDE

    我目前正在为我的公司开发 PL SQL 存储过程 我想使用代码修订控制系统来跟踪我和其他开发人员所做的更改 我喜欢 GIT 的所有优点 包括分布式 scm 功能 有没有支持GIT的PL SQL开发IDE 目前 我正在使用 JDevelope
  • 从 C# 调用非托管函数:我应该传递 StringBuilder 还是使用不安全代码?

    我有一个 C 程序 需要将字符缓冲区传递给非托管函数 我发现了两种似乎可靠的方法 但我不确定应该选择哪一种 这是非托管函数的签名 extern C declspec dllexport int getNextResponse char bu
  • 关于 C++ 中异常的缺点

    我正在阅读 Google C 风格指南 并在其中感到困惑例外情况 http google styleguide googlecode com svn trunk cppguide xml showone Exceptions Excepti
  • XPath 中“//”和“/”的区别?

    我正在尝试使用 python selenium 的 XPath I used 这个链接 http www jetairways com EN SG Home aspx尝试教程中的一些 XPath 所以我尝试了 XPath 的这两种变体 这个
  • Vuejs 刷新时路由重定向

    当我在浏览器中使用刷新按钮或点击f5在键盘上 它不会刷新我的页面 而是重定向到主页 Code router js import Vue from vue import VueRouter from vue router import sto
  • 接口依赖关系[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 当您创建一个接口并且知道您将依赖另一个接口时 您是否会将构造函数作为接口的一部分 就我而言 我想创建 我可以向客户端提供一个 IClientRe
  • 伊莎贝尔案例分析

    如何在伊莎贝尔中应用案例分析 我正在寻找类似的东西apply induct x 用于归纳 案例分析通常是通过cases方法 另见索引中的 案例 方法 伊莎贝尔 伊萨尔参考手册 http isabelle in tum de website
  • 使用 grep 列出目录中的条目

    我试图列出目录中名称仅包含大写字母的所有条目 目录需要附加 bin bash cd testfiles ls grep r 由于 grep 默认情况下仅查找大写字母 对吗 因此我只是在 testfiles 下的目录中递归搜索仅包含大写字母的
  • 将 nlog 中的记录添加到 dataType = date 的字段

    I use nlogdll 写入数据库 Oracle 与实体框架在行中 logger Log logLevel try 我在 nlog 日志中收到以下错误 文字与模板字符串不匹配 代码是 SetPropGDC LogEntity NLog
  • 在 Windows 上使用 Xerces 3.0.1 和 C++ 编写 XML

    我编写了以下函数来使用 Xerces 3 0 1 创建 XML 文件 如果我使用 foo xml 或 foo xml 的文件路径调用此函数 它会很好用 但如果我传入 c foo xml 然后我在这一行得到一个异常 XMLFormatTarg
  • C#通过OLEDB从Excel中提取格式化文本

    我正在从一个大的数据中读取数据excel已格式化文本的文件 我将数据提取到DataTable对象通过oleDBConnection GetOleDbSchemaTable 但提取的数据不包含任何格式化信息 我的要求是 我只需要提取非删除线文
  • 如何使用 Android Gradle 插件 0.7 配置 NDK

    新的 Android gradle 插件 0 7 似乎包含了对 NDK 的新支持 但在文档中几乎没有提到它 我找到的唯一参考是一个名为ndkSanAngeles https android googlesource com platform