JNI та Gradle в Android Studio


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/adt-dev/-GbnrQA8f7M
Скотт Барта,

Відповіді:


122

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 переходить у кланг, оскільки 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підкаталог. Це велика проблема.

ndkDirectory

Це траплялося кілька разів, але android.ndkDirectoryце правильний спосіб отримати папку після 1.1. Міграція проектів Gradle до версії 1.0.0 . Якщо ви використовуєте експериментальну або старовинну версію плагіна, ваш пробіг може відрізнятися.


2
Один твік, який мені довелося зробити: визначити завдання поза блоком Android. Інакше градле скаржився.
Utkarsh Sinha

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 підтримує компіляцію 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 з libs /.

До речі, у вас виникли проблеми із компіляцією для x86? Я бачу, ви не включили його у свій APP_ABI.


2
Хороша відповідь! Ти мене вразив! =) іншого рішення, я просто не знайшов.
Влад Гудницький 02

1
Я сам переживав те саме питання. Я вирішив це, додавши порожній файл .c до каталогу ./app/src/main/jni (я назвав файл "utils.c", але ви можете назвати його як завгодно ...). З тих пір все працює нормально. Я нічого не змінював у файлі налаштувань Gradle.
GeertVc

4
Якому батькові я повинен це поставити? Я спробував кілька місць, і я отримую "не вдається вирішити символ: jni".
Ендрю Вайлд,

ви повинні поставити його підandroid { }
ph0b

1
Ви можете отримати докладнішу інформацію з моєї статті: ph0b.com/android-studio-gradle-and-ndk-integration
ph0b

8

У моєму випадку я працюю у 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'
}

Розширення файлів! Потрібно любити Windows.
Камерон Лоуелл Палмер,

Що всередині src/main/libs? Що, якщо це ще один підкаталог, такий як armeabi
Ігор Ганапольський

Список файлів .so, які я міг би додати, у моєму минулому проекті це були opencv libs для android.
Брайан Нг

Ви можете знайти каталог 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 (8 липня 2016 р.). Див. Примітки до випуску за посиланням вище.


Не могли б Ви детальніше розповісти "Хоча нам довелося почекати до 2.2.3 для підтримки Application.mk"? Це Preview3, або ..?
Себастьян Рот,

@SebastianRoth: Так, це попередній перегляд 3. На сьогодні, 8 серпня 2015 року, випущена Android Studio 2.2 Beta, яка забезпечує підтримку зовнішньої власної збірки.
Вишах Амарнатт

6

Моє питання на OSX - це версія gradle. Gradle ігнорував мій Android.mk. Отже, щоб замінити цей параметр і використовувати замість нього мій make, я ввів цей рядок:

sourceSets.main.jni.srcDirs = []

всередині androidтегу в build.gradle.

Я витратив на це багато часу!


1
Я не розумію, тому що @CameronLowellPalmer це вказав. Або градле тлумачить sourceSets.main { jni.srcDirs = [] }якимось іншим чином, ніж sourceSets.main.jni.srcDirs = []? Я вважаю, що ваша проблема була десь в іншому місці, і ви випадково її виправили.
Oliver Hausler

@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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.