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

use*_*906 48 android-ndk android-studio android-gradle-plugin

新的Android gradle插件(0.7)似乎包含对NDK的新支持,但在文档中几乎没有提到它(我发现的唯一参考是一个名为test的测试 ndkSanAngeles).

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

  • 出了什么问题:任务执行失败':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'
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

谢谢.

rea*_*ore 76

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

在Prebuilt Native Libraries中简单链接,只需在任务中添加一个ndk部分即可.例如,我在productFlavors中添加了它.abiFilter是libs存储的文件夹名称.abiFilters意味着来自逗号分隔列表的libs将被添加到你的最终APK中(理论上你可以有"armeabi","armeabi-v7a","x86"和" mips"all in one APK,O/S将在安装时选择支持的架构lib:

productFlavors {
    arm {
        ndk {
            abiFilters "armeabi", "armeabi-v7a"
        }
    }
    x86 {
        ndk {
            abiFilter "x86"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,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
Run Code Online (Sandbox Code Playgroud)

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

static
{
    loadLibrary("myNative");
}
Run Code Online (Sandbox Code Playgroud)

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

static
{
    loadLibrary("myDependency");
    loadLibrary("myNative");
}
Run Code Online (Sandbox Code Playgroud)

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

buildTypes {
    debug {
        ndk {
            abiFilters "armeabi", "armeabi-v7a"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过预先构建,我指的是您下载的第三方库或使用NDK工具链或您自己的ARM工具链(而不是ndk-build脚本本身)构建的库.

在API 18中,他们修复了一个长期存在的体系结构问题,该问题阻止了本机lib加载程序"自动"加载依赖项,因为它不知道应用程序的lib目录(安全性原因等).在API 18及更高版本中,如果myNative依赖于上面的myDependency,则可以只调用loadLibrary("myNative"),操作系统将处理加载myDependency.但是,请不要依赖此,直到运行API 17及更低版本的设备的市场渗透率达到您可接受的低数量.


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

如前所述,将local.properties中的ndk.dir值设置为指向NDK home.有谁知道你是否可以直接在local.properties中使用env vars?:)

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

ndk {
    moduleName "myNDKModule"
    stl "stlport_shared"
    ldLibs "log", "z", "m"
    cFlags "-I/some/include/path"
}
Run Code Online (Sandbox Code Playgroud)

这是当前支持的类型(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系统在临时目录中构建.

Sidetracked一秒.moduleName应匹配jni目录下项目中的ac或cpp文件,如:

[project]/[app]/src/main/jni/myNDKModule.cpp
Run Code Online (Sandbox Code Playgroud)

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

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

你不能在Mac OSX上设置ANDROID_NDK_HOME(见下文),但是从我做过的一些研究看来,这可能仍适用于其他操作系统.它将被删除.

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

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

但正如您所看到的,Google决定他们根本不想让IDE使用环境变量; 我仍然坚持这个决定.更新local.properties指向可以在我的gradle脚本中加载的绝对路径让我的生活变得痛苦,我仍然没有想到如何做(但是看起来并不那么难).这意味着我要么强迫我的团队成员使用与我相同的路径,玩链接,让他们在每次拉回购物时都输入它们,或添加自动化脚本.我认为这是一个糟糕的决定,任何依赖于env vars的开发人员都会花费时间,这些开发人员在微观层面可能很小,但在宏观层面却很大.

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

有关Google的更多详细示例,请参阅以下最新示例(搜索jni或ndk):https://docs.google.com/viewer ?a = v& pid = sites & ssidid = YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA


使用NDK的跨平台胖子APK:

最后,使用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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在试图用gradle和本地第三方库创建一个干净的庄园中的胖二进制之后,我终于得出结论,谷歌Play的内置多架构支持APK无论如何都是最好的路径,所以创建每个架构的个人APK.

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

// 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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这是我见过的有关ndk设置的最详细的逐步介绍.谷歌应该聘请你重写他们所有的文件. (2认同)

use*_*906 34

找到了答案.包括ndk.dir=path/to/ndklocal.properties文件中做了伎俩.

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

  • 将一些东西手动放入`local.properties`文件是一个好主意,因为它说"这个文件是由Android Studio自动生成的.不要修改这个文件 - 你的更改会被删除!" ? (5认同)

ste*_*nsk 15

您还可以设置ANDROID_NDK_HOME环境变量