JNI und Gradle in Android Studio

75

Ich versuche, meiner App nativen Code hinzuzufügen. Ich habe alles so ../main/jniwie es in meinem Eclipse-Projekt war. Ich habe ndk.dir=...zu meinem hinzugefügt local.properties. Ich habe noch nichts anderes getan (ich bin mir nicht sicher, was sonst noch benötigt wird. Wenn ich also etwas verpasst habe, lass es mich wissen). Wenn ich versuche zu bauen, erhalte ich folgende Fehlermeldung:

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.

Was muss ich tun?

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
Fredley
quelle
Es gibt hier einen Thread, der Ihnen einen kleinen Einblick geben kann: groups.google.com/forum/#!topic/adt-dev/-GbnrQA8f7M
Scott Barta

Antworten:

122

Gradle Build Tools 2.2.0+ - Das NDK kommt der Bezeichnung "Magie" am nächsten.

Ich bin froh, dass 2.2.x der Gradle Build Tools herausgekommen ist und jetzt einfach funktioniert. Der Schlüssel ist das externalNativeBuildund zeigt auf das ndkBuildPfadargument auf ein Android.mkoder ändert ndkBuildauf cmakeund zeigt auf das Pfadargument auf ein CMakeLists.txtBuild-Skript.

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

Weitere Informationen finden Sie auf der Google-Seite zum Hinzufügen von nativem Code .

Nachdem dies richtig eingerichtet wurde, können ./gradlew installDebugSie loslegen. Sie müssen sich auch darüber im Klaren sein, dass sich das NDK in Clang bewegt, da gcc im Android-NDK jetzt veraltet ist.

Integration und Bereinigung von Android Studio - DEPRECATED

Die anderen Antworten zeigen den richtigen Weg auf, um die automatische Erstellung von Android.mkDateien zu verhindern , aber sie gehen nicht den zusätzlichen Schritt einer besseren Integration in Android Studio. Ich habe die Möglichkeit hinzugefügt, tatsächlich von der Quelle zu bereinigen und zu erstellen, ohne zur Befehlszeile gehen zu müssen. Ihre local.propertiesDatei muss habenndk.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'
}

Das src/main/jniVerzeichnis nimmt ein Standardlayout des Projekts an. Es sollte der Verwandte von diesem build.gradleDateispeicherort zum jniVerzeichnis sein.

Gradle - für diejenigen, die Probleme haben

Überprüfen Sie auch diese Antwort zum Stapelüberlauf .

Es ist wirklich wichtig, dass Ihre Gradle-Version und das allgemeine Setup korrekt sind. Wenn Sie ein älteres Projekt haben, empfehle ich dringend, ein neues mit dem neuesten Android Studio zu erstellen und zu sehen, was Google als Standardprojekt betrachtet. Verwenden Sie auch gradlew. Dies schützt den Entwickler vor einer Nichtübereinstimmung der Gradle-Version. Schließlich muss das Gradle-Plugin korrekt konfiguriert sein.

Und Sie fragen, was ist die neueste Version des Gradle-Plugins? Überprüfen Sie die Tools-Seite und bearbeiten Sie die Version entsprechend.

Endprodukt - /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()
    }
}

Stellen Sie sicher gradle wrapper, dass die gradlewDatei und das gradle/wrapperUnterverzeichnis generiert werden . Das ist eine große Sache.

ndkDirectory

Dies ist einige Male aufgetreten, android.ndkDirectoryist jedoch der richtige Weg, um den Ordner nach 1.1 abzurufen. Gradle-Projekte auf Version 1.0.0 migrieren . Wenn Sie eine experimentelle oder alte Version des Plugins verwenden, kann Ihr Kilometerstand variieren.

Cameron Lowell Palmer
quelle
2
Eine Änderung, die ich vornehmen musste: Definieren Sie die Aufgaben außerhalb des Android-Blocks. Ansonsten beschwerte sich gradle.
Utkarsh Sinha
1
Sie können def ndkDir = android.plugin.ndkFoldereine einfachere Möglichkeit verwenden, um einen Verweis auf das Android-Plugin zu erhalten.
Ben Lings
1
Ich sehe das gleiche Problem wie @UtkarshSinha oder ähnliches. Die Aufgaben sind grau unterstrichen und die folgende Meldung wird angezeigt: "Argumenttypen können nicht abgeleitet werden". Gradle ist auf dem neuesten Stand (2.2.1), und ich glaube, das Problem liegt an einer anderen Stelle wie in sourceSets.main. Die Symbole jni und jniLibs sind mit der Meldung "Symbol kann nicht aufgelöst werden" unterstrichen. Wenn ich die Aufgaben außerhalb der Android-Blöcke verschiebe, wird kein Fehler angezeigt, aber ich denke nicht, dass dies richtig sein kann.
Oliver Hausler
3
Ich musste hinzufügen, def ndkDir = plugins.getPlugin('com.android.application').sdkHandler.ndkFolderdamit es funktioniert
SztupY
2
Sie, Sir, verdienen einen Keks!
DBM
73

gradle unterstützt die ndk-Kompilierung, indem eine weitere Android.mk-Datei mit absoluten Pfaden zu Ihren Quellen generiert wird. NDK unterstützt absolute Pfade seit r9 unter OSX, r9c unter Windows, daher müssen Sie Ihr NDK auf r9 + aktualisieren.

Möglicherweise treten andere Probleme auf, da die NDK-Unterstützung durch gradle vorläufig ist. In diesem Fall können Sie die ndk-Kompilierung von gradle deaktivieren, indem Sie Folgendes einstellen:

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

um ndk-build selbst aufrufen und libs von libs / integrieren zu können.

Übrigens, haben Sie Probleme beim Kompilieren für x86? Ich sehe, dass Sie es nicht in Ihren APP_ABI aufgenommen haben.

ph0b
quelle
2
Gute Antwort! Du hast meinen Tag gerettet! =) Eine andere Lösung habe ich einfach nicht gefunden.
Vlad Hudnitsky
1
Ich habe das gleiche Problem selbst erlebt. Ich habe es gelöst, indem ich eine leere .c-Datei in das Verzeichnis ./app/src/main/jni eingefügt habe (ich habe die Datei "utils.c" genannt, aber Sie können sie so nennen, wie Sie möchten ...). Seitdem funktioniert alles gut. Ich habe nichts an der Gradle-Einstellungsdatei geändert.
GeertVc
4
Unter welchen Elternteil soll ich das stellen? Ich habe ein paar Stellen ausprobiert und erhalte die Meldung "Symbol kann nicht aufgelöst werden: jni".
Andrew Wyld
Sie sollten es unterandroid { }
ph0b
1
Weitere Informationen finden Sie in meinem Artikel: ph0b.com/android-studio-gradle-and-ndk-integration
ph0b
8

In meinem Fall bin ich unter Windows und das Befolgen der obigen Antwort von Cameron funktioniert nur, wenn Sie den vollständigen Namen des ndk-build verwenden, der ndk-build.cmd lautet . Ich muss das Projekt bereinigen und neu erstellen und dann den Emulator neu starten, bevor die App funktioniert (eigentlich habe ich das Beispiel HelloJni aus NDK in Android Studio importiert). Stellen Sie jedoch sicher, dass der Pfad zum NDK keinen Speicherplatz enthält .

Schließlich ist mein build.gradle wie folgt vollständig aufgelistet:

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'
}
Brian Ng
quelle
Dateierweiterungen! Ich muss Windows lieben.
Cameron Lowell Palmer
Was ist drin src/main/libs? Was ist, wenn es ein anderes Unterverzeichnis wie armeabi ist
IgorGanapolsky
Liste der .so-Dateien, die ich hinzufügen könnte, in meinem letzten Projekt war es opencv libs für Android.
Brian Ng
Sie finden das NDK-Verzeichnis über die Umgebungsvariable NDK_ROOT_DIRCTORY. Wenn Android Studio den Wert ignoriert, sollten Sie einen Fehlerbericht einreichen.
JWW
7

Android Studio 2.2 bietet die Möglichkeit, ndk-build und cMake zu verwenden. Wir mussten jedoch bis 2.2.3 auf die Application.mk-Unterstützung warten. Ich habe es versucht, es funktioniert ... obwohl meine Variablen nicht im Debugger angezeigt werden. Ich kann sie trotzdem über die Kommandozeile abfragen.

Sie müssen so etwas tun:

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

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

NB: Die zusätzliche Verschachtelung von externalNativeBuildinnen defaultConfigwar eine bahnbrechende Änderung, die mit Android Studio 2.2 Preview 5 (8. Juli 2016) eingeführt wurde. Siehe die Versionshinweise unter dem obigen Link.

Ronnie
quelle
Könnten Sie näher auf "Allerdings mussten wir bis 2.2.3 auf die Unterstützung von Application.mk warten" eingehen? Ist das Preview3 oder ..?
Sebastian Roth
@SebastianRoth: Ja, es ist Vorschau 3. Ab heute, dem 8. August 2015, wurde Android Studio 2.2 Beta veröffentlicht, das Unterstützung für externe native Builds bietet.
Vyshakh Amarnath
6

Mein Problem unter OSX war die Gradle-Version. Gradle ignorierte mein Android.mk. Um diese Option zu überschreiben und stattdessen mein Make zu verwenden, habe ich folgende Zeile eingegeben:

sourceSets.main.jni.srcDirs = []

innerhalb des androidTags in build.gradle.

Ich habe viel Zeit damit verschwendet!

Paschalis
quelle
1
Ich verstehe nicht, weil @CameronLowellPalmer dies angegeben hatte. Oder interpretiert gradle sourceSets.main { jni.srcDirs = [] }anders als sourceSets.main.jni.srcDirs = []? Ich glaube, Ihr Problem war woanders und Sie haben es versehentlich behoben.
Oliver Hausler
@OliverHausler diese beiden Aussagen sind äquivalent.
Cameron Lowell Palmer
2

Im Modul build.gradle im Aufgabenfeld wird eine Fehlermeldung angezeigt, es sei denn, ich verwende:

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

Ich sehe Leute benutzen

def ndkDir = android.plugin.ndkFolder

und

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

Aber keines davon funktionierte, bis ich es in das Plugin änderte, das ich tatsächlich importierte.

videogameboy76
quelle
Sie finden das NDK-Verzeichnis über die Umgebungsvariable NDK_ROOT_DIRCTORY. Wenn Android Studio den Wert ignoriert, sollten Sie einen Fehlerbericht einreichen.
JWW