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

person fredley    schedule 13.01.2014    source источник
comment
Здесь есть ветка, которая может дать вам небольшую информацию: groups .google.com/forum/#!topic/adt-dev/-GbnrQA8f7M   -  person Scott Barta    schedule 13.01.2014


Ответы (6)


Gradle Build Tools 2.2.0+ — самый близкий NDK, которого когда-либо называли «волшебным».

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

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

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

Интеграция очистки и сборки 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 — для тех, у кого проблемы

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

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

ндкдиректори

Это встречалось несколько раз, но android.ndkDirectory — правильный способ получить папку после версии 1.1. Миграция проектов Gradle в версию 1.0.0< /а>. Если вы используете экспериментальную или старую версию плагина, ваш пробег может отличаться.

person Cameron Lowell Palmer    schedule 01.11.2014
comment
Мне пришлось сделать одну настройку: определить задачи вне блока Android. В противном случае Gradle жаловался. - person Utkarsh Sinha; 25.12.2014
comment
Вы можете использовать def ndkDir = android.plugin.ndkFolder как более простой способ получить ссылку на плагин Android. - person Ben Lings; 02.01.2015
comment
@UtkarshSinha это кажется неправильным. Можете ли вы убедиться, что используете последнюю версию Gradle? - person Cameron Lowell Palmer; 02.01.2015
comment
Я вижу ту же проблему, что и @UtkarshSinha или что-то подобное. Задачи подчеркнуты серым цветом, и отображается следующее сообщение: Невозможно вывести типы аргументов. Gradle обновлен (2.2.1), и я считаю, что проблема в другом месте, так как в sourceSets.main символы jni и jniLibs подчеркнуты сообщением Не удается разрешить символ. Когда я перемещаю задачи за пределы блоков Android, ошибка не отображается, но я не думаю, что это может быть правильно. - person Oliver Hausler; 11.01.2015
comment
Вы уверены, что используете тот град, о котором думаете? - person Cameron Lowell Palmer; 11.01.2015
comment
@CameronLowellPalmer Я не понимаю, что ты здесь сделал. Какой файл нужно отредактировать, а что добавить? не могли бы вы взглянуть на мою библиотеку здесь: github.com/AndroidDeveloperLB/AndroidJniBitmapOperations? Я пытался сделать то, что вы написали, но я думаю, что кое-что из того, что вы написали, нужно изменить в моем случае. - person android developer; 16.01.2015
comment
@androiddeveloper После того, как я исправил gradle-wrapper.properties для использования gradle-2.2-all.zip, все пошло хорошо. - person Cameron Lowell Palmer; 16.01.2015
comment
@CameronLowellPalmer Я не понимаю, что вы написали. Не могли бы вы опубликовать на Github, что нужно изменить? - person android developer; 16.01.2015
comment
@androiddeveloper проверьте свои запросы на включение. Я сделал два коммита. - person Cameron Lowell Palmer; 16.01.2015
comment
@UtkarshSinha Я бы проверил обновления, которые я внес в ответ, для уточнения. Это может позволить вам правильно настроить ваш проект. - person Cameron Lowell Palmer; 16.01.2015
comment
@GregEnnis Я буду рад получить наивысший балл. :) - person Cameron Lowell Palmer; 23.01.2015
comment
У меня проблема: android.plugin.ndkFolder возвращает 'null'. Как я могу определить свой путь ndk? Спасибо. - person jmrodrigg; 27.01.2015
comment
Решено: я не добавил ndk.dir в local.properties. Теперь я занимаюсь проблемой. Текущая поддержка NDK устарела. - person jmrodrigg; 27.01.2015
comment
Это не проблема. Это информационное предупреждение. Google может наконец улучшить поддержку NDK. - person Cameron Lowell Palmer; 27.01.2015
comment
Спасибо, вы были правы, проблема была в другом. Я активировал трассировку стека и понял, что мне нужно запустить «ndk-build.cmd». Забыл поставить расширение. Теперь он работает отлично. - person jmrodrigg; 27.01.2015
comment
да. Теперь я ищу что-нибудь для запуска разных командной строки в зависимости от ОС... есть идеи? - person jmrodrigg; 28.01.2015
comment
@CameronLowellPalmer Спасибо. - person jmrodrigg; 29.01.2015
comment
@jmrodrigg ничего не приходит на ум. Кроссплатформенность сложная. Самой большой проблемой, с которой вы столкнетесь, может быть поиск необходимых инструментов. Я думаю, что в какой-то момент потребуется README и вмешательство пользователя. - person Cameron Lowell Palmer; 29.01.2015
comment
@jmrodrigg, вы можете использовать org.apache.tools.ant.taskdefs.condition.Os, как я сделал в этом примере: gist.github.com/ph0b/9e59058ac59cac104398. Также, если в путь пользователя добавлен ndk-build, нет необходимости указывать его расположение. - person ph0b; 27.04.2015
comment
:) Как только вы обнаружите, что используете муравья, разве вы не потерпели неудачу? - person Cameron Lowell Palmer; 28.04.2015
comment
Мне пришлось добавить def ndkDir = plugins.getPlugin('com.android.application').sdkHandler.ndkFolder, чтобы он работал - person SztupY; 15.06.2015
comment
Спасибо SztupY! Сборка AStudio 141.2006197 жаловалась на то, что не находит плагины. Ваше предложение решило эту проблему! - person Zoccadoum; 17.06.2015
comment
+1 к SztupY тоже. Я использую плагин android-gradle 1.2.3 и gradle 2.4, android.plugin.ndkFolder приводит к тому, что свойство плагина не найдено. Я создавал библиотеку, поэтому мне помогли plugins.getPlugin('com.android.library').sdkHandler.ndkFolder. @SztupY, где вы найдете документацию для этого плагина? Откуда ты знаешь, какие там свойства? - person Some Noob Student; 20.06.2015
comment
@SomeNoobStudent Я просто гуглил и нашел похожую тему где-то еще. То, что они написали, вообще не сработало, но вот эта строчка пригодилась - person SztupY; 20.06.2015
comment
@CameronLowellPalmer - да, командная строка (ndk_build.sh) работала нормально, но сборка gradle жаловалась, пока я не добавил предложение SztupY. - person Zoccadoum; 23.06.2015
comment
@CameronLowellPalmer Как изменить это решение, чтобы использовать «NDK_DEBUG = 1» для типа сборки отладки и «NDK_DEBUG = 0» для типа сборки выпуска? - person DeathlessHorsie; 26.07.2015
comment
@DeathlessHorsie одним из способов было бы создать две отдельные задачи, вы можете назвать их, например, buildNativeRelease и buildNativeDebug. - person Cameron Lowell Palmer; 27.07.2015
comment
@CameronLowellPalmer да, я уже пробовал. Я попробовал этот код: tasks.whenTaskAdded { задача -> if (task.getName(). } } Я вижу две проблемы: 1. когда я настраиваю библиотеку на отладку, она также запускает задачу выпуска. 2. когда я строю после переключения типа сборки, он не компилирует собственный код снова, поэтому файлы отладки/выпуска *.so остаются. Иногда он перестраивает нативный код. - person DeathlessHorsie; 27.07.2015
comment
@DeathlessHorsie Попробуйте это: tasks.whenTaskAdded { задача -› if (task.name == 'compileDebugNdk') { task.dependsOn buildNativeDebug } } Дайте мне знать, если это сработает. - person Cameron Lowell Palmer; 28.07.2015
comment
@CameronLowellPalmer, делающий compileDebugNdk зависимым от buildNativeDebug, кажется лучшим выбором, чем generateDebugSources. Однако это не решает моих проблем. 1. Кажется, это ограничение Android Studio/Gradle. Есть возможность сделать конкретный модуль из контекстного меню в представлении проекта в студии. Этот параметр запускает только задачу отладки. 2. Похоже, это ограничение ndk-build. Он просто не распознает изменение параметра NDK_DEBUG. - person DeathlessHorsie; 28.07.2015
comment
@DeathlessHorsie, насколько я понимаю, вы реализовали вышеуказанное решение для отдельных compileDebugNdk и compileReleaseNdk с отсутствующим флагом отладки для последнего, но проблема в том, что он не перекомпилируется при переключении между отладкой и выпуском, правильно? Вы можете сделать часть NDK всегда чистой перед компиляцией, что не идеально, но это та же проблема, если вы пытаетесь сделать то же самое в командной строке с помощью make. Кстати, я компилирую с помощью ./gradlew assembleDebug или assembleRelease. - person Cameron Lowell Palmer; 28.07.2015
comment
@CameronLowellPalmer спасибо, я обновил сборку, и она работает правильно. - person Zoccadoum; 30.07.2015
comment
Как определить sourceSets.main при использовании экспериментального плагина? - person Shailen; 30.10.2015
comment
Вы, сэр, заслуживаете печенья! - person dbm; 20.12.2015
comment
Комментарий This is not necessary unless you have precompiled libraries in your project. вводит в заблуждение. Мне пришлось добавить его, иначе сгенерированный общий объект не попадет в окончательный файл apk. - person Torsten Robitzki; 29.01.2016
comment
@jww вам следует перейти на 2.2.1 и забыть о NDK_ROOT_DIRECTORY. - person Cameron Lowell Palmer; 15.10.2016
comment
@CameronLowellPalmer - Насколько мне известно, команда NDK по-прежнему рекомендует это. Если Android Studio игнорирует это, отправьте отчет об ошибке. Похоже, что Android Studio просто делает дерьмо на ходу. - person jww; 15.10.2016
comment
@jww на самом деле, я делаю большую часть компиляции через прямую градацию в командной строке. Плагин gradle 2.2.1 для Android устраняет большинство проблем, связанных с NDK, которые заставляли вас вручную вызывать ndk-build, и добавляет поддержку cmake. - person Cameron Lowell Palmer; 15.10.2016
comment
вместо того, чтобы скрывать исходники C++, я предпочитаю просто отключить задачи complieXxxNdk, добавив следующие чары в build.gradle: tasks.all { task -> if (task.name.contains('compileDebugNdk') || task.name.contains('compileReleaseNdk')) { task.enabled = false } } - person Alex Cohn; 07.12.2016
comment
@AlexCohn отсутствует контекст этого комментария - person Cameron Lowell Palmer; 07.12.2016
comment
@AlexCohn ах, да. Однако в этом больше нет необходимости, если вы обновите инструменты сборки до версии 2.2.x+. - person Cameron Lowell Palmer; 07.12.2016
comment
Бывают еще ситуации, когда externalNativeBuild использовать нельзя - person Alex Cohn; 07.12.2016
comment
@AlexCohn да, я уверен. Просто большинству людей, вероятно, больше ничего не нужно, и это путь вперед! :) - person Cameron Lowell Palmer; 07.12.2016

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

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

person ph0b    schedule 14.01.2014
comment
Хороший ответ! Вы сделали мой день! =) другого решения я просто не нашел. - person Vlad Hudnitsky; 03.02.2014
comment
Сам испытал ту же проблему. Я решил эту проблему, добавив пустой файл .c в каталог ./app/src/main/jni (я назвал файл utils.c, но вы можете называть его как хотите...). С тех пор все работает нормально. Я ничего не менял в файле настроек Gradle. - person GeertVc; 17.09.2014
comment
Под каким родителем я должен поставить это? Я пробовал пару мест, и я не могу разрешить символ: jni. - person Andrew Wyld; 19.01.2015
comment
вы должны поставить его под android { } - person ph0b; 20.01.2015
comment
Это сработало для меня. Может кто-нибудь объяснить ответ в деталях? - person Amit Gupta; 06.07.2015
comment
Вы можете получить более подробную информацию из моей статьи: ph0b.com/android-studio- gradle-and-ndk-интеграция - person ph0b; 06.07.2015

В моем случае я работаю в Windows, и ответ Кэмерона выше работает только в том случае, если вы используете полное имя 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'
}
person Brian Ng    schedule 09.03.2015
comment
Расширения файлов! Должен любить Windows. - person Cameron Lowell Palmer; 21.12.2015
comment
Что внутри src/main/libs? Что, если это другой подкаталог, например armeabi? - person IgorGanapolsky; 11.05.2016
comment
Список файлов .so, которые я мог бы добавить, в моем прошлом проекте это были opencv libs для Android. - person Brian Ng; 14.05.2016

В 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 (8 июля 2016 г.). См. примечания к выпуску по ссылке выше.

person Ronnie    schedule 16.06.2016
comment
Не могли бы вы уточнить, хотя нам пришлось ждать до 2.2.3 для поддержки Application.mk? Это Preview3 или..? - person Sebastian Roth; 30.06.2016
comment
@SebastianRoth: Да, это предварительная версия 3. На сегодняшний день, 8 августа 2015 г., выпущена бета-версия Android Studio 2.2, которая обеспечивает поддержку внешней собственной сборки. - person Vyshakh Amarnath; 10.08.2016

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

sourceSets.main.jni.srcDirs = []

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

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

person Paschalis    schedule 26.02.2014
comment
Я не понимаю, потому что @CameronLowellPalmer указал это. Или gradle интерпретирует sourceSets.main { jni.srcDirs = [] } как-то иначе, чем sourceSets.main.jni.srcDirs = []? Я считаю, что ваша проблема была где-то в другом месте, и вы случайно исправили ее. - person Oliver Hausler; 11.01.2015
comment
@OliverHausler эти два утверждения эквивалентны. - person Cameron Lowell Palmer; 26.01.2015

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

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

person videogameboy76    schedule 26.06.2015