JNI и Gradle в Android Studio

Я пытаюсь добавить собственный код в свое приложение. У меня есть все в ../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

Ответ 1

Gradle Build Tools 2.2.0+ - Ближайший NDK когда-либо назывался волшебным

Пытаясь избежать экспериментальных и откровенно сытых по горло NDK и всех его хакеров, я рад, что появился 2.2.x из Gradle Build Tools, и теперь он просто работает. Ключ - это аргумент externalNativeBuild и pointing ndkBuild path в Android.mk или изменить ndkBuild на cmake и указать аргумент пути в 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'
        //}
    }
}

Для более подробной информации проверьте страницу Google по добавлению собственного кода.

После правильной настройки вы можете ./gradlew installDebug и выйти. Вам также необходимо знать, что NDK переходит на clang, поскольку gcc теперь устарел в Android NDK.

Интеграция в Android Studio Clean и Build - DEPRECATED

Другие ответы указывают на правильный способ предотвратить автоматическое создание файлов 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 - для тех, у кого есть проблемы

Также проверьте этот ответ на переполнение стека.

Очень важно, чтобы ваша версия 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

gradle поддерживает компиляцию ndk, создавая другой файл Android.mk с абсолютными путями к вашим источникам. NDK поддерживает абсолютные пути с r9 на OSX, r9c в Windows, поэтому вам нужно обновить NDK до r9 +.

Вы можете столкнуться с другими проблемами, поскольку поддержка NDK с помощью gradle является предварительной. Если это так, вы можете деактивировать компиляцию ndk из gradle, установив:

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

чтобы иметь возможность вызывать ndk-build самостоятельно и интегрировать библиотеки libs/.

btw, у вас есть компиляция проблем для x86? Я вижу, вы не включили его в свой APP_ABI.

Ответ 3

В моем случае я нахожусь в Windows и после ответа от Cameron выше работает только, если вы используете полное имя ndk-build, которое ndk-build.cmd. Я должен очистить и перестроить проект, а затем перезапустить эмулятор, прежде чем заставить приложение работать (на самом деле я импортировал образец HelloJni из NDK в Android Studio). Однако убедитесь, что путь к NDK не содержит пробела.

Наконец, мой 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'
}

Ответ 4

Моя проблема с OSX была версией gradle. gradle игнорировал мой Android.mk. Итак, чтобы переопределить этот параметр и вместо этого использовать мой make, я ввел эту строку:

sourceSets.main.jni.srcDirs = []

внутри тега android в build.gradle.

Я потратил много времени на это!

Ответ 5

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

NB: Дополнительное вложение externalNativeBuild внутри defaultConfig было изменением, внесенным в Android Studio 2.2 Preview 5 (8 июля 2016 г.). См. Примечания к выпуску по указанной выше ссылке.

Ответ 6

В модуле 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()

но ни один из них не работал до тех пор, пока я не изменил его на плагин, который я фактически импортировал.