Android Studio 中的 JNI 和 Gradle

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

我需要做什么?

安卓:

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)

应用:

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi armeabi-v7a
APP_PLATFORM := android-8
126431 次浏览

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+.

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:

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

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

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

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:

sourceSets.main.jni.srcDirs = []

inside of the android tag in build.gradle.

I have wasted lot of time on this!

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

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 externalNativeBuild and pointing ndkBuild path argument at an Android.mk or change ndkBuild to cmake and point the path argument at a CMakeLists.txt build script.

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.

After this is setup correctly you can ./gradlew installDebug and 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.

Android Studio Clean and Build Integration - DEPRECATED

The other answers do point out the correct way to prevent the automatic creation of Android.mk files, 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.properties file will need to have 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/jni directory assumes a standard layout of the project. It should be the relative from this build.gradle file location to the jni directory.

Gradle - for those having issues

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.

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

Final product - /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 wrapper generates the gradlew file and gradle/wrapper subdirectory. This is a big gotcha.

ndkDirectory

This has come up a number of times, but android.ndkDirectory is 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.

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 emulator before 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.

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

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

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

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.

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.

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

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