Android Studio中的JNI和Gradle


75

我正在尝试将本机代码添加到我的应用中。我拥有../main/jni了Eclipse项目中的所有内容。我已添加ndk.dir=...local.properties。我还没有做其他任何事情(我不确定实际上还需要什么,所以如果我错过了什么,请告诉我)。当我尝试构建时,出现以下错误:

Execution failed for task ':app:compileDebugNdk'.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
    /Users/me/android-ndk-r8e/ndk-build NDK_PROJECT_PATH=null 
APP_BUILD_SCRIPT=/Users/me/Project/app/build/ndk/debug/Android.mk APP_PLATFORM=android-19 
NDK_OUT=/Users/me/Project/app/build/ndk/debug/obj 
NDK_LIBS_OUT=/Users/me/Project/app/build/ndk/debug/lib APP_ABI=all

  Error Code:
    2
  Output:
    make: *** No rule to make target `/Users/me/Project/webapp/build/ndk/debug//Users/me/Project/app/src/main/jni/jni_part.cpp',
 needed by `/Users/me/Project/app/build/ndk/debug/obj/local/armeabi-v7a/objs/webapp//Users/me/Project/app/src/main/jni/jni_part.o'.  
Stop.

我需要做什么?

Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

# OpenCV
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
include .../OpenCV-2.4.5-android-sdk/sdk/native/jni/OpenCV.mk

LOCAL_MODULE    := native_part
LOCAL_SRC_FILES := jni_part.cpp
LOCAL_LDLIBS +=  -llog -ldl

include $(BUILD_SHARED_LIBRARY)

Application.mk:

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi armeabi-v7a
APP_PLATFORM := android-8

这里有一个线程可以为您提供一些见解:groups.google.com/forum
topic

Answers:


122

Gradle Build Tools 2.2.0+-NDK最接近被称为“魔术”

为了避免对NDK及其所有黑客程序进行实验性的坦率地厌倦,我很高兴Gradle Build Tools 2.2.x出现了,现在可以正常使用了。关键是将externalNativeBuildand指向ndkBuildpath参数指向Android.mk或将其更改ndkBuildcmake指向CMakeLists.txtbuild脚本并将其指向。

android {
    compileSdkVersion 19
    buildToolsVersion "25.0.2"

    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 19

        ndk {
            abiFilters 'armeabi', 'armeabi-v7a', 'x86'
        }

        externalNativeBuild {
            cmake {
                cppFlags '-std=c++11'
                arguments '-DANDROID_TOOLCHAIN=clang',
                        '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_STL=gnustl_static',
                        '-DANDROID_ARM_NEON=TRUE',
                        '-DANDROID_CPP_FEATURES=exceptions rtti'
            }
        }
    }

    externalNativeBuild {
        cmake {
             path 'src/main/jni/CMakeLists.txt'
        }
        //ndkBuild {
        //   path 'src/main/jni/Android.mk'
        //}
    }
}

有关更多详细信息,请访问Google有关添加本机代码的页面

正确设置之后,您就可以./gradlew installDebug离开了。您还需要注意,由于Android NDK中不推荐使用gcc,因此NDK正在迁移到clang。

Android Studio清理和构建集成-已弃用

其他答案的确指出了防止自动创建Android.mk文件的正确方法,但是它们并没有采取与Android Studio更好地集成的额外步骤。我增加了从源代码进行实际清理和构建的功能,而无需转到命令行。您的local.properties文件将需要具有ndk.dir=/path/to/ndk

apply plugin: 'com.android.application'

android {
    compileSdkVersion 14
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.example.application"
        minSdkVersion 14
        targetSdkVersion 14

        ndk {
            moduleName "YourModuleName"
        }
    }

    sourceSets.main {
        jni.srcDirs = [] // This prevents the auto generation of Android.mk
        jniLibs.srcDir 'src/main/libs' // This is not necessary unless you have precompiled libraries in your project.
    }

    task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
        def ndkDir = android.ndkDirectory
        commandLine "$ndkDir/ndk-build",
                '-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source
                '-j', Runtime.runtime.availableProcessors(),
                'all',
                'NDK_DEBUG=1'
    }

    task cleanNative(type: Exec, description: 'Clean JNI object files') {
        def ndkDir = android.ndkDirectory
        commandLine "$ndkDir/ndk-build",
                '-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source
                'clean'
    }

    clean.dependsOn 'cleanNative'

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

dependencies {
    compile 'com.android.support:support-v4:20.0.0'
}

src/main/jni目录采用项目的标准布局。它应该是从该build.gradle文件位置到jni目录的相对位置。

摇篮-对于那些有问题的人

还要检查此堆栈溢出答案

正确的gradle版本和常规设置非常重要。如果您有较旧的项目,我强烈建议您使用最新的Android Studio创建一个新项目,并查看Google认为标准项目是什么。另外,请使用gradlew。这样可以防止开发人员遇到gradle版本不匹配的情况。最后,必须正确配置gradle插件。

您问gradle插件的最新版本是什么?检查工具页面并相应地编辑版本。

最终产品-/build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.

// Running 'gradle wrapper' will generate gradlew - Getting gradle wrapper working and using it will save you a lot of pain.
task wrapper(type: Wrapper) {
    gradleVersion = '2.2'
}

// Look Google doesn't use Maven Central, they use jcenter now.
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

确保gradle wrapper生成gradlew文件和gradle/wrapper子目录。这是一个大陷阱。

ndkDirectory

这已经出现了很多次,但这android.ndkDirectory是在1.1之后获取文件夹的正确方法。将Gradle项目迁移到版本1.0.0。如果您使用的是实验版或旧版插件,则里程可能会有所不同。


2
我必须做的一项调整:在android块之外定义任务。否则gradle抱怨。
Utkarsh Sinha 2014年

1
您可以使用def ndkDir = android.plugin.ndkFolder一种更简单的方式来获取对android插件的引用。
Ben Lings

1
我看到与@UtkarshSinha相同的问题或类似的问题。任务用灰色下划线标出,并显示以下消息:“无法推断参数类型”。Gradle是最新的(2.2.1),我认为问题出在sourceSets.main中的其他地方,符号jni和jniLibs用消息“无法解析符号”下划线。当我将任务移到Android块之外时,不会显示任何错误,但我认为这可能不正确。
奥利弗·豪斯勒

3
我必须添加内容def ndkDir = plugins.getPlugin('com.android.application').sdkHandler.ndkFolder才能使其正常工作
-SztupY

2
先生,您该当吃饼干!
dbm

73

gradle通过生成另一个具有源绝对路径的Android.mk文件来支持ndk编译。自OSX上的r9,Windows上的r9c以来,NDK支持绝对路径,因此您需要将NDK升级到r9 +。

由于gradle的NDK支持是初步的,因此您可能会遇到其他麻烦。如果是这样,您可以通过设置从gradle禁用ndk编译:

sourceSets.main {
    jni.srcDirs = []
    jniLibs.srcDir 'src/main/libs'
}

才能自己调用​​ndk-build并从libs /集成lib。

顺便说一句,您对x86编译有任何问题吗?我发现您尚未将其包含在您的APP_ABI中。


2
好答案!你让我今天一整天都感觉很好!=)另一种解决方案,我只是没有找到。
Vlad Hudnitsky

1
我自己也遇到过同样的问题。我通过将一个空的.c文件添加到./app/src/main/jni目录中来解决了该问题(我将其命名为“ utils.c”,但是您可以随意调用它...)。从那时起,一切正常。我没有对Gradle设置文件进行任何更改。
GeertVc 2014年

4
我该放哪一位父母?我已经尝试了几个地方,但得到“无法解析符号:jni”。
安德鲁·怀尔德

您应该将其放在android { }
ph0b 2015年

1
您可以从我的文章中获得更多详细信息:ph0b.com/android-studio-gradle-and-ndk-integration
ph0b 2015年

8

就我而言,我在Windows上,并且仅当您使用ndk-build的全名(即ndk-build.cmd)时,才能遵循上述Cameron的回答。我必须清理并重建项目,然后在使应用程序运行之前重新启动模拟器(实际上,我是从NDK导入示例HelloJni到Android Studio中的)。但是,请确保NDK的路径不包含space

最后,我的build.gradle完整列出如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "com.example.hellojni"
        minSdkVersion 4
        targetSdkVersion 4

        ndk {
            moduleName "hello-jni"
        }

        testApplicationId "com.example.hellojni.tests"
        testInstrumentationRunner "android.test.InstrumentationTestRunner"
    }
    sourceSets.main {
        jni.srcDirs = [] // This prevents the auto generation of Android.mk
//        sourceSets.main.jni.srcDirs = []
        jniLibs.srcDir 'src/main/libs' // This is not necessary unless you have precompiled libraries in your project.
    }

    task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
        def ndkDir = android.plugin.ndkFolder
        commandLine "$ndkDir/ndk-build.cmd",
                '-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source
                '-j', Runtime.runtime.availableProcessors(),
                'all',
                'NDK_DEBUG=1'
    }

    task cleanNative(type: Exec, description: 'Clean JNI object files') {
        def ndkDir = android.plugin.ndkFolder
        commandLine "$ndkDir/ndk-build.cmd",
                '-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source
                'clean'
    }

    clean.dependsOn 'cleanNative'

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

}


dependencies {
    compile 'com.android.support:support-v4:21.0.3'
}

文件扩展名!要爱Windows。
卡梅隆·洛厄尔·帕尔默

里面是src/main/libs什么?如果它是另一个子目录(如armeabi)
怎么

我可能会添加的.so文件列表,在我过去的项目中是android的opencv库。
布赖恩·伍

您可以通过环境变量找到NDK目录NDK_ROOT_DIRCTORY。如果Android Studio忽略该值,则应提交一个错误报告。
jww

7

Android Studio 2.2可以使用ndk-build和cMake。但是,我们必须等到2.2.3才能获得Application.mk支持。我已经尝试过,它可以工作...尽管,我的变量没有显示在调试器中。我仍然可以通过命令行查询它们。

您需要执行以下操作:

externalNativeBuild{
   ndkBuild{
        path "Android.mk"
    }
}

defaultConfig {
  externalNativeBuild{
    ndkBuild {
      arguments "NDK_APPLICATION_MK:=Application.mk"
      cFlags "-DTEST_C_FLAG1"  "-DTEST_C_FLAG2"
      cppFlags "-DTEST_CPP_FLAG2"  "-DTEST_CPP_FLAG2"
      abiFilters "armeabi-v7a", "armeabi"
    }
  } 
}

请参阅http://tools.android.com/tech-docs/external-c-builds

注意:externalNativeBuild内部多余的嵌套defaultConfig是Android Studio 2.2 Preview 5(2016年7月8日)引入的一项重大更改。请参阅上面链接的发行说明。


您能否详细说明“尽管,​​我们必须等到2.2.3才能获得Application.mk支持”?那是Preview3,还是..?
塞巴斯蒂安·罗斯

@SebastianRoth:是的,它是预览版。3.从今天起到2015年8月8日,Android Studio 2.2 Beta已发布,它为外部本机版本提供支持。
Vyshakh Amarnath

6

我在OSX上的问题是gradle版本。Gradle忽略了我的Android.mk。因此,为了覆盖此选项,并改用我的品牌,我输入了以下行:

sourceSets.main.jni.srcDirs = []

在中的android标记内build.gradle

我在此上浪费了很多时间!


1
我不明白,因为@CameronLowellPalmer已指定了此名称。还是gradle解释sourceSets.main { jni.srcDirs = [] }的方式与sourceSets.main.jni.srcDirs = []?我相信您的问题在其他地方,您不小心修复了它。
奥利弗·豪斯勒

@OliverHausler这两个语句是等效的。
卡梅隆·洛厄尔·帕尔默

2

在模块build.gradle的任务字段中,除非使用以下命令,否则会出现错误:

def ndkDir = plugins.getPlugin('com.android.application').sdkHandler.getNdkFolder()

我看到有人在用

def ndkDir = android.plugin.ndkFolder

def ndkDir = plugins.getPlugin('com.android.library').sdkHandler.getNdkFolder()

但是在我将其更改为我实际上要导入的插件之前,这些方法都无效。


您可以通过环境变量找到NDK目录NDK_ROOT_DIRCTORY。如果Android Studio忽略该值,则应提交一个错误报告。
jww
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.