当前位置: 首页 > 知识库问答 >
问题:

如何用Android Gradle插件0.7配置NDK

向安福
2023-03-14

新的Android gradle插件(0.7)似乎包括了对NDK的新支持,但在文档中几乎没有提到它(我找到的唯一参考是一个名为< code>ndkSanAngeles的测试)。

看起来gradle正在寻找NDK,我已经将其包含在PATH中。然而,构建项目失败

    < li >哪里出错:任务执行失败':ogl tests: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'
        }
    }
}

谢了。

共有3个答案

洪研
2023-03-14

您还可以设置ANDROID_NDK_HOME环境变量

农诚
2023-03-14

找到答案了。在< code>local.properties文件中包含< code>ndk.dir=path/to/ndk就可以做到这一点。

更新:在最新版本的Android Studio上,您可以直接在项目结构中设置值

湛宝
2023-03-14

浏览gradle插件代码,我发现以下内容有助于我使用NDK和预建的本地库:

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

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 中(例如 debug 或 release 或其他任何你可能使用的东西)。例如:

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

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

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

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

如前所述,在local.properties中设置ndk.dir值以指向ndk主目录。有人知道可以直接在local.properties中使用env vars吗?:)

在您的版本中。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/path/to/someOtherLib -wl,-no-whole-archive”

在这一行中,您只需标记到第一个参数的末尾以添加不以 -l 开头的参数,因此您现在可以完成。在上面的例子中,我将整个静态库链接到我的 NDK 模块中,以便在 Java 中使用。我已经要求谷歌开发者添加额外的功能,以允许这样做,甚至能够将你自己的 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

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

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

你不能在Mac OSX上设置ANDROID_NDK_HOME(见下文),但是从我所做的一些研究来看,这可能在其他操作系统上仍然有效。不过,它会被移除。

我想发表评论,但目前还没有名声。丹尼斯,环境变量被完全忽略,而不仅仅是被覆盖。事实上,您没有得到任何环境变量。据我所知,AndroidStudio IDE只使用几个特定的环境变量(选中System.getenv()并从渐变脚本中打印出来)创建自己的环境。

我在这里把它写成一个错误,因为使用 env vars 从 cmd 行构建得很好:
https://code.google.com/p/android/issues/detail?id=65213

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

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

有关Google的更详细示例,以下是最新示例(搜索jni或ndk): https://docs.google.com/viewer?a=v

< br >使用NDK的跨平台fat APKs:

最后,使用gradle有一个缺点,无法提供自己的Android。mk文件,以便您只能在第三方本地库中从单个架构链接到NDK。注意我说的是“链接”。您可以使用“abiFilters”命令在多个架构中构建NDK模块(上面的moduleName),它们将被放置在您的应用程序中,以便相同的APK可以用于多个架构。如果您需要链接到自己的第三方库,甚至根据您的架构为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和原生第三方库在一个干净的庄园里创建一个肥胖的二进制文件之后,我终于得出结论,谷歌游戏对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。

 类似资料:
  • 问题内容: Jenkins(Hudson)具有StarTeam插件。如何正确配置? 我已经安装了此插件,但是在构建项目时出现此错误: java.lang.NoClassDefFoundError:com / starbase / starteam / Folder 机器是Windows Server2008。我在文件夹中安装了StarTeam SDK 。 问题答案: 在插件文档页面上有一个提示:

  • 如https://cloud . Google . com/app engine/docs/standard/Java/tools/Gradle-reference上所述,AppEngine Gradle插件提供如下配置: 使用 时,这样的配置应该是什么样子? 我正在查看AppEngine任务,但不明白如何将其连接到适当的Kotlin DSL设置。 编辑 当简单地将上述块添加到<code>buil

  • 我不知道如何在带有kotlin DSL的gradle构建中使用shadow插件。所有文档都使用groovy dsl。 这是build.gradle.kts的内容: 这是的内容 但是当我尝试进行 时,我收到此错误: 我认为这很奇怪,因为我已经在参数中输入了应用程序主类。 我试着加上这个: 但是当我尝试使用此选项构建时,它抱怨它找不到类型。 我到底做错了什么?

  • 丰富的过滤器插件的存在是 logstash 威力如此强大的重要因素。名为过滤器,其实提供的不单单是过滤的功能。在本章我们就会重点介绍几个插件,它们扩展了进入过滤器的原始数据,进行复杂的逻辑处理,甚至可以无中生有的添加新的 logstash 事件到后续的流程中去!

  • Codec 是 logstash 从 1.3.0 版开始新引入的概念(Codec 来自 Coder/decoder 两个单词的首字母缩写)。 在此之前,logstash 只支持纯文本形式输入,然后以过滤器处理它。但现在,我们可以在输入 期处理不同类型的数据,这全是因为有了 codec 设置。 所以,这里需要纠正之前的一个概念。Logstash 不只是一个input | filter | outpu