Android Studio 中的 JNI 和 Gradle

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21096819/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:58:55  来源:igfitidea点击:

JNI and Gradle in Android Studio

androidjava-native-interfacegradleandroid-studioandroid-ndk-r7

提问by fredley

I'm trying to add native code to my app. I have everything in ../main/jnias it was in my Eclipse project. I have added ndk.dir=...to my local.properties. I haven't done anything else yet (I'm not sure what else is actually required, so if I've missed something let me know). When I try and build I get this error:

我正在尝试将本机代码添加到我的应用程序中。我拥有所有东西,../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.

What do I need to do?

我需要做什么?

Android.mk:

安卓.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:

应用程序.mk:

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

回答by Cameron Lowell Palmer

Gradle Build Tools 2.2.0+ - The closest the NDK has ever come to being called 'magic'

Gradle 构建工具 2.2.0+ - NDK 最接近被称为“魔法”的工具

In trying to avoid experimental and frankly fed up with the NDK and all its hackery I am happy that 2.2.x of the Gradle Build Tools came out and now it just works. The key is the externalNativeBuildand pointing ndkBuildpath argument at an Android.mkor change ndkBuildto cmakeand point the path argument at a CMakeLists.txtbuild script.

为了避免实验性的并坦率地厌倦了 NDK 及其所有黑客行为,我很高兴 Gradle 构建工具的 2.2.x 版出来了,现在它可以正常工作了。关键是将路径参数externalNativeBuild和指向ndkBuild路径参数Android.mk或更改ndkBuildcmake并将路径参数指向CMakeLists.txt构建脚本。

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'
        //}
    }
}

For much more detail check Google's page on adding native code.

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

After this is setup correctly you can ./gradlew installDebugand off you go. You will also need to be aware that the NDK is moving to clang since gcc is now deprecated in the Android NDK.

正确设置后,您就可以./gradlew installDebug开始了。您还需要注意 NDK 正在转移到 clang,因为 gcc 现在在 Android NDK 中已被弃用。

Android Studio Clean and Build Integration - DEPRECATED

Android Studio Clean 和 Build 集成 - 已弃用

The other answers do point out the correct way to prevent the automatic creation of Android.mkfiles, but they fail to go the extra step of integrating better with Android Studio. I have added the ability to actually clean and build from source without needing to go to the command-line. Your local.propertiesfile will need to have ndk.dir=/path/to/ndk

其他答案确实指出了防止自动创建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'
}

The src/main/jnidirectory assumes a standard layout of the project. It should be the relative from this build.gradlefile location to the jnidirectory.

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

Gradle - for those having issues

Gradle - 对于那些有问题的人

Also check this Stack Overflow answer.

另请检查此堆栈溢出答案

It is really important that your gradle version and general setup are correct. If you have an older project I highly recommend creating a new one with the latest Android Studio and see what Google considers the standard project. Also, use gradlew. This protects the developer from a gradle version mismatch. Finally, the gradle plugin must be configured correctly.

您的 gradle 版本和常规设置正确非常重要。如果您有一个较旧的项目,我强烈建议您使用最新的 Android Studio 创建一个新项目,并查看 Google 认为的标准项目。另外,使用gradlew. 这可以保护开发人员免受 gradle 版本不匹配的影响。最后,必须正确配置gradle插件。

And you ask what is the latest version of the gradle plugin? Check the tools pageand edit the version accordingly.

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

Final product - /build.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()
    }
}

Make sure gradle wrappergenerates the gradlewfile and gradle/wrappersubdirectory. This is a big gotcha.

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

ndkDirectory

ndk目录

This has come up a number of times, but android.ndkDirectoryis the correct way to get the folder after 1.1. Migrating Gradle Projects to version 1.0.0. If you're using an experimental or ancient version of the plugin your mileage may vary.

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

回答by ph0b

gradle supports ndk compilation by generating another Android.mk file with absolute paths to your sources. NDK supports absolute paths since r9 on OSX, r9c on Windows, so you need to upgrade your NDK to r9+.

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

You may run into other troubles as NDK support by gradle is preliminary. If so you can deactivate the ndk compilation from gradle by setting:

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

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

to be able to call ndk-build yourself and integrate libs from libs/.

能够自己调用 ndk-build 并从 libs/ 集成库。

btw, you have any issue compiling for x86 ? I see you haven't included it in your APP_ABI.

顺便说一句,您在为 x86 编译时有任何问题吗?我看到您没有将它包含在您的 APP_ABI 中。

回答by Brian Ng

In my case, I'm on Windows and following the answer by Cameron above only works if you use the full name of the ndk-build which is ndk-build.cmd. I have to clean and rebuild the project, then restart the emulatorbefore getting the app to work (Actually I imported the sample HelloJni from NDK, into Android Studio). However, make sure the path to NDK does not contain space.

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

Finally, my build.gradle is full listed as below:

最后,我的 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'
}

回答by Paschalis

My issue on OSX it was gradle version. Gradle was ignoring my Android.mk. So, in order to override this option, and use my make instead, I have entered this line:

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

sourceSets.main.jni.srcDirs = []

sourceSets.main.jni.srcDirs = []

inside of the androidtag in build.gradle.

中的android标签内部build.gradle

I have wasted lot of time on this!

我在这上面浪费了很多时间!

回答by Ronnie

Android Studio 2.2 came out with the ability to use ndk-build and cMake. Though, we had to wait til 2.2.3 for the Application.mk support. I've tried it, it works...though, my variables aren't showing up in the debugger. I can still query them via command line though.

Android Studio 2.2 推出了使用 ndk-build 和 cMake 的功能。不过,我们不得不等到 2.2.3 才能获得 Application.mk 的支持。我试过了,它有效……不过,我的变量没有显示在调试器中。我仍然可以通过命令行查询它们。

You need to do something like this:

你需要做这样的事情:

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"
    }
  } 
}

See http://tools.android.com/tech-docs/external-c-builds

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

NB:The extra nesting of externalNativeBuildinside defaultConfigwas a breaking change introduced with Android Studio 2.2 Preview 5 (July 8, 2016). See the release notes at the above link.

注意:externalNativeBuild内部的额外嵌套defaultConfig是 Android Studio 2.2 预览版 5(2016 年 7 月 8 日)引入的重大更改。请参阅上述链接中的发行说明。

回答by videogameboy76

In the module build.gradle, in the task field, I get an error unless I use:

在模块 build.gradle 的任务字段中,除非我使用,否则我会收到错误消息:

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

I see people using

我看到人们使用

def ndkDir = android.plugin.ndkFolder

and

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

but neither of those worked until I changed it to the plugin I was actually importing.

但是在我将其更改为我实际导入的插件之前,这些都不起作用。