2013-12-19 10 views
47

Das neue Android Gradle Plugin (0.7) scheint neue Unterstützung für das NDK zu enthalten, aber in der Dokumentation gibt es kaum oder gar nicht zu erwähnen (die einzige Referenz, die ich fand, ist ein Test genannt ndkSanAngeles).Wie konfiguriert man NDK mit Android Gradle Plugin 0.7

Es sieht aus wie Gradle ist auf der Suche nach der NDK, die ich in meinem Pfad enthalten haben. Allerdings scheitert das Projekt Gebäude mit

  • Was schief gelaufen ist: Ausführung fehlgeschlagen für Aufgabe ': OGLTests: compileDefaultFlavorDebugNdk'. NDK nicht
  • konfiguriert

Wie kann ich das NDK in gradle konfigurieren?

Mein aktueller build.gradle sieht wie folgt aus:

task nativeLibsToJar(type: Zip, description: 'create a jar with native libs') { 
    destinationDir file("$buildDir/native-libs") 
    baseName 'native-libs' 
    extension 'jar' 
    from fileTree(dir: 'src/main/libs', include: '**/*.so') 
    from fileTree(dir: 'src/main/libs', include: '**/gdb*') 
    into 'lib/' 
} 

tasks.withType(JavaCompile) { 
    compileTask -> compileTask.dependsOn nativeLibsToJar 
} 

dependencies { 
    compile fileTree(dir: "$buildDir/native-libs", include: '*.jar') 
} 

android { 
    compileSdkVersion 19 
    buildToolsVersion '19.0.0' 

    defaultConfig { 
     minSdkVersion 14 
     targetSdkVersion 19 
     versionCode 1 
     versionName "0.1" 

    } 
    buildTypes { 
     release { 
      runProguard false 
     } 
     debug { 
      // jniDebugBuild true 
      runProguard false 
      debuggable true 
     } 
    } 
    productFlavors { 
     defaultFlavor { 
      proguardFile 'proguard-rules.txt' 
     } 
    } 
} 

Dank.

Antwort

34

Die Antwort gefunden. Einschließlich ndk.dir=path/to/ndk in der Datei local.properties tat den Trick.

Aktualisierung: In den neuesten Versionen von Android Studio können Sie den Wert direkt im Projektstruktur> SDK-Speicherort festlegen.

+0

NDK Vergessen Sie nicht, Ihre Antwort zu akzeptieren – orip

+5

Ist es eine gute Idee, etwas von Hand in die 'local.properties' Datei zu setzen, da er sagt "Diese Datei wird automatisch von Android Studio erstellt. Ändern Sie diese Datei nicht - IHRE ÄNDERUNGEN WERDEN GELÖSCHT!" ? –

15

Sie auch vor ANDROID_NDK_HOME Umgebungsvariable

+0

, dass ich getan habe, aber wie es in der Anwendung widerspiegelt? Trotzdem bekomme ich denselben Fehler. – CoDe

+0

Dies funktioniert nicht in den neuesten Build-Tools 2.2+ –

0

Wie kommentiert gesetzt, ndk.dir Zugabe = in local.properties hilft. Interessanterweise habe ich festgestellt, dass local.properties jeden Wert außer Kraft setzt, der für die Umgebungsvariable ANDROID_NDK_HOME, gesetzt wurde, selbst wenn in der local.properties nicht ndk.dir konfiguriert ist. (zumindest mit dem Gadget android Plugin v 0.7.3).

Dieser verwirrenden ist als Android Studio local.properties überschreiben kann, und es scheint nicht, einen Weg zu bieten ndk.dir :(

+0

Ich verstehe nicht, was Sie über unspezifizierte sagen. "ndk.dir in local.properties überschreibt jeden Wert ... auch wenn Sie ndk.dir in local.properties nicht konfiguriert haben" - sagen Sie, was?!?! Nicht spezifizierte Werte überschreiben eine Umgebungsvariable .... sagen Sie wirklich, dass die Umgebungsvariable ignoriert wird? Oder was vermisse ich? – Dennis

+0

Ja, ich stimme @Dennis zu, es gibt kein Problem mit dem Verlassen von local.properties. – Merk

76

Blick durch den gradle Plugin-Code zu konfigurieren, fand ich die folgende, die dazu beigetragen benutz ich sowohl NDK und vorkompilierte nativen Bibliotheken:

einfach Links in vorgefertigte native Bibliotheken, nur einen NDK Abschnitt zu Ihrer Aufgabe hinzufügen zum Beispiel, ich habe es in productFlavors unten hinzugefügt der abiFilter ist der Ordner mit dem libs nennen.. sind gespeichert in. abiFilters bedeutet, dass beide Libs aus der kommagetrennten Liste zu deinem endgültigen APK hinzugefügt werden (also könntest du theoretisch "armeabi", "armea" V7A“, "x86" und "mips" alle in einer APK, und die O/S würde die unterstützte Architektur lib wählt bei der Installation):

productFlavors { 
    arm { 
     ndk { 
      abiFilters "armeabi", "armeabi-v7a" 
     } 
    } 
    x86 { 
     ndk { 
      abiFilter "x86" 
     } 
    } 
} 

In diesem Beispiel wird der Arm baut eine APK erstellen mit die V5- und V7A-Armbibliotheken darin, und der x86-Build wird ein APK mit nur x86-Bibliotheken darin erstellen. Dies wird nach den nativen Bibliotheken in Ihrem Projekt jniLibs-Verzeichnis suchen. Das Verzeichnis jniLibs sollte wie das alte jni-Verzeichnis strukturiert sein, d. H.:

[project]/[app]/src/main/jniLibs/armeabi/libmyNative.so 
[project]/[app]/src/main/jniLibs/armeabi-v7a/libmyNative.so 
[project]/[app]/src/main/jniLibs/x86/libmyNative.so 

Dann können Sie es in Java wie folgt laden:

Jetzt
static 
{ 
    loadLibrary("myNative"); 
} 

, lassen Sie uns hängt von einer anderen eine native Bibliothek sagen. Sie müssen (wenn Ihr min API API 17 oder niedrigere Einstellung) laden die abhängigen Bibliotheken zuerst:

static 
{ 
    loadLibrary("myDependency"); 
    loadLibrary("myNative"); 
} 

Sie können auch die NDK platzieren {} Abschnitt in Ihrem DefaultConfig oder eine buildType (wie Debug oder Release oder was auch immer sonst können Sie verwenden). Zum Beispiel:

buildTypes { 
    debug { 
     ndk { 
      abiFilters "armeabi", "armeabi-v7a" 
     } 
    } 
} 

von prebuilt, ich meine 3rd-Party-Libs Sie heruntergeladen oder eine Bibliothek Sie das NDK Werkzeugkette oder Ihr eigenes ARM Toolchain (nicht der NDK-Build-Skript selbst) gebaut werden. In API 18 haben sie ein seit langem bestehendes Architekturproblem behoben, das verhinderte, dass der native lib loader Abhängigkeiten automatisch "lud", weil er das lib-Verzeichnis der Anwendung nicht kannte (Sicherheitsgründe usw.). Wenn in API 18 und höher myNative von myDependency abhängig ist, können Sie einfach loadLibrary ("myNative") aufrufen und das Betriebssystem wird myDependency laden. VERLASSEN Sie sich jedoch nicht darauf, bis die Marktdurchdringung von Geräten mit API 17 und niedriger für Sie akzeptabel ist.


Um explizit NDK Bibliotheken Von Quelle in der aktuellen Version von Android Studio bauen, können Sie folgendes tun:

Stellen Sie den ndk.dir Wert in Ihrem local.properties zu-zu-Punkt NDK home wie zuvor erwähnt. Weiß jemand, ob Sie env vars direkt in local.properties verwenden können? :)

In Ihrer build.gradle Datei, um so etwas zu Ihrer Aufgabe hinzufügen (wieder kann sein DefaultConfig, debuggen, Freigabe, ein productFlavor, usw.):

ndk { 
    moduleName "myNDKModule" 
    stl "stlport_shared" 
    ldLibs "log", "z", "m" 
    cFlags "-I/some/include/path" 
} 

Dies ist die grundlegende Struktur mit Derzeit unterstützte Typen (moduleName, stl, ldLibs und cFlags). Ich schaute und fand nicht mehr als das. Es gibt ein Problem, das ich mit ldLibs glaube, weil es automatisch "-l" an die Vorderseite jedes obigen Feldes hinzufügt. Sie können es aber täuschen (ich musste) mit: ldLibs "log -lz -lm -Wl, -whole-archive -l/pfad/zu/someOtherLib -Wl, -no-whole-arch"

In dieser Zeile markieren Sie einfach bis zum Ende des ersten Parameters, um Parameter hinzuzufügen, die nicht mit -l beginnen, damit Sie sich jetzt zurechtfinden. Im obigen Fall binde ich eine ganze statische Bibliothek in mein NDK-Modul zur Verwendung von innerhalb von Java. Ich habe den Google-Entwickler gebeten, zusätzliche Funktionen hinzuzufügen, um diese oder sogar die Möglichkeit zu schaffen, Ihre eigene Android.mk-Datei in den NDK-Build-Prozess zu integrieren, aber da dies alles neu ist, kann es eine Weile dauern.

Derzeit, was auch immer Sie in build.gradle setzen löscht das Temp-Build-Verzeichnis und erstellt es jedes Mal neu, so dass, wenn Sie den Graddle Android-Plugin-Quellcode herunterladen und ändern möchten (es wäre lustig), gibt es einige "machen Es ist nötig, dass deine Sachen in den Build kopiert werden. Das android-Skript, das diese ndk-Unterstützung bereitstellt, generiert im Wesentlichen eine Android.mk-Datei und erstellt das NDK-System in einem temporären Verzeichnis.

Für eine Sekunde abgelenkt.Der Modulname sollte ac oder CPP-Datei in Ihrem Projekt unter dem jni Verzeichnis wie entsprechen:

[project]/[app]/src/main/jni/myNDKModule.cpp 

Der stl Wert sollte auf einen Wert von „stlport_shared“ oder „stlport_static“ gesetzt werden, wenn Sie die STLport Bibliotheken verwenden für C++. Sie können stl verlassen, wenn Sie keine erweiterte C++ - Unterstützung benötigen. Denken Sie daran, dass Android standardmäßig nur grundlegende C++ - Unterstützung bietet. Zeigen Sie für andere unterstützte C++ - Bibliotheken Ihre NDK-Dokumentationsrichtlinien in dem NDK an, das Sie downloaden. Beachten Sie, dass die Variable libstlport_shared.so aus dem Verzeichnis sources/cxx-stl/stlport/libs des NDK in die lib-Verzeichnisse Ihres APK kopiert wird, indem Sie sie hier auf stlport_shared setzen. Es behandelt auch den Include-Pfad im Compiler (technisch macht das nicht alles, sondern das Android-NDK-Build-System). Also legen Sie keine eigene Kopie von stlport in Ihr jniLibs-Verzeichnis.

Schließlich denke ich, cFlags ist ziemlich offensichtlich.

Sie können ANDROID_NDK_HOME nicht auf dem Mac OSX (siehe unten) einstellen, aber aus einigen Recherchen, die ich gemacht habe, scheint das vielleicht auch auf anderen Betriebssystemen zu funktionieren. Es wird jedoch entfernt.

Ich wollte kommentieren, aber noch nicht den Ruf haben. Dennis, die Umgebungsvariablen werden komplett ignoriert, nicht nur überschrieben. Tatsächlich erhalten Sie keine Ihrer Umgebungsvariablen. Soweit ich das beurteilen kann, erstellt die Android Studio-IDE eine eigene Umgebung mit nur wenigen spezifischen Umgebungsvariablen (überprüfen Sie System.getenv() und drucken Sie sie aus einem Skript aus).

Ich schrieb dies als Fehler hier, weil env mit Vars baut aus cmd Linie fein:
https://code.google.com/p/android/issues/detail?id=65213

aber wie Sie sehen können, Google beschlossen sie Umgebungsvariablen nicht wollen, von der IDE verwendet wird alle; Ich bin immer noch auf dem Zaun auf dieser Entscheidung. Es macht mir das Leben schwer, local.properties zu aktualisieren, um auf absolute Pfade zu zeigen, die in meine Skripts geladen werden können, die ich bis jetzt noch nicht herausgefunden habe (aber ich habe noch nicht so viel geschaut). Das bedeutet, dass ich meine Teammitglieder entweder dazu zwinge, den gleichen Pfad wie ich zu verwenden, mit Links zu spielen, sie alle bei jedem Abruf des Repos einzutippen oder ein Automatisierungsskript hinzuzufügen. Ich glaube, es ist eine schlechte Entscheidung, die Zeit für alle Entwickler kosten wird, die sich auf env vars verlassen, die auf der Mikroebene klein, auf der Makroebene aber sehr groß sind.

erdschleifen, ich glaube, die IDE wird in Kürze mit der Fähigkeit, den NDK-Ordner Pfad zu Ihrem Projekt hinzuzufügen, und es wird automatisch die local.properties-Datei daraus (zumindest würde es keinen Sinn, wenn sie Daran hatte ich nicht gedacht.

Weitere detaillierte Beispiele von Google, hier sind die neuesten Beispiele (für jni oder NDK suchen): https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA


plattformübergreifende Fett APKs mit NDK:

Schließlich gibt es ein Nachteil bei der Verwendung von Gradle und nicht in der Lage, Ihre eigene Android.mk-Datei bereitzustellen, so dass Sie nur in nativen Bibliotheken von Drittanbietern von einer einzigen Architektur zu Ihrem NDK verknüpfen können. Hinweis Ich sagte "Link in". Sie können die NDK-Module (moduleName oben) in mehreren Architekturen mit dem Befehl "abiFilters" erstellen, und sie werden in Ihrer App platziert, sodass dieselbe APK auf mehreren Architekturen verwendet werden kann. Wenn Sie in Ihre eigenen 3rd-Party-Bibliotheken einbinden müssen oder je nach Ihrer Architektur unterschiedliche Werte für cFlags haben, gibt es keinen einfachen Weg.

Ich versuchte das Folgende und es schien zuerst zu arbeiten, aber dann fand ich heraus, dass es einfach das NDK baute, indem es alles aus den beiden ndk-Abschnitten anfügte (oder so, es hat irgendwie mehrere Architekturbibliotheken gebaut) :

android { 
    compileSdkVersion 23 
    buildToolsVersion '23.0.1' 
    defaultConfig { 
     minSdkVersion 14 
     targetSdkVersion 23 
     versionCode 28 
     versionName "3.0" 
    } 
    buildTypes { 
     def commonLibs = " -lfoo -lbar -lwhatever" 
     def armV7LibsDir = "/whatever/armv7a/libs" 
     def armX86LibsDir = "/whatever/x86/libs" 
     def armV7IncDir = "/whatever/armv7a/include" 
     def x86IncDir = "/whatever/x86/include" 
     debug { 
      ndk { 
       cFlags = "-I" + armV7IncDir 
       moduleName "myNativeCPPModule" 
       stl "stlport_shared" 
       abiFilter "armeabi-v7a" 
       ldLibs "log -L" + armV7LibsDir + commonLibs 
      } 
      ndk { 
       cFlags = "-I" + armX86IncDir 
       moduleName "myNativeCPPModule" 
       stl "stlport_shared" 
       abiFilter "x86" 
       ldLibs "log -L" + armX86LibsDir + commonLibs 
      } 
     } 
    } 
} 

nach viel Schmerz ein fettes binary in einem sauberen Herrenhaus mit gradle und nativer 3rd-Party-libs zu schaffen versucht, ich kam schließlich zu dem Schluss, dass Google Play-integrierte Unterstützung Multi-Architektur für APKs ist wirklich die Die beste Route, um trotzdem zu gehen, also erstelle individuelle APKs für jede Architektur.

Also habe ich mehrere buildTypes, keine Produktaromen erstellt und den folgenden Code hinzugefügt, um den Versionscode für jeden Typ zu generieren.

// This is somewhat nasty, but we need to put a "2" in front of all ARMEABI-V7A builds, a "3" in front of 64-bit ARM, etc. 
// Google Play chooses the best APK based on version code, so if a device supports both X86 and 
// ARM, it will choose the X86 APK (preferred because Inky ARM running on an X86 with Houdini ARM Emulator crashes in our case) 
android.applicationVariants.all { variant -> 
    if (variant.buildType.name.equals('release')) { 
     variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debug')) { 
     variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugArmV8a')) { 
     variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseArmV8a')) { 
     variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugMips')) { 
     variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseMips')) { 
     variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugMips64')) { 
     variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseMips64')) { 
     variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugX86')) { 
     variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseX86')) { 
     variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugX86_64')) { 
     variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseX86_64')) { 
     variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode 
    } 
} 

Alles was Sie jetzt tun müssen, um den Wert für version in Ihrem DefaultConfig Objekt festgelegt ist, wie Sie es normalerweise tun würde, und dies hängt es an das Ende der architekturspezifischen Version Zeichenfolge, basierend auf Buildtyp. Die Versionszeichenfolge bleibt für alle Builds gleich, variiert jedoch den Code, um eine Rangfolge von ARM bis hin zu X86_64 anzugeben. Es ist ein bisschen hackisch oder hart-codiert, aber es macht den Job erledigt. Beachten Sie, dass Sie damit bis zu 999 Versionen erhalten. Wenn Sie also mehr benötigen, multiplizieren Sie die obigen Zahlen mit 10, wobei Sie nicht sicher sind, welchen Maximalwert Sie für den Versionscode eingeben können.

In meinem Fall haben wir ein ziemlich komplexes Build-System. Wir erstellen CPython für 9 Architekturen, von denen 3 Android sind, erstellen dann eine Reihe eigener Bibliotheken und verknüpfen sie für jede Architektur zu einer einzigen Bibliothek. Wir verwenden die Build-Tools ndk, Build und Autake und Python, um alles anstelle von Android.mk-Dateien zu erstellen. Die endgültigen Bibliotheken werden dann in eine einzige JNI-Schnittstellen-CPP-Datei (oben als myNativeCPPModule bezeichnet) verknüpft. Ein Klick auf den Button und alles ist auf einmal erstellt, sehr schönes Android Studio.

+0

Sehr hilfreich. Danke in der Tat. Übrigens, kennen Sie das Standardsuchverzeichnis für statische Benutzerbibliotheken? Ich entscheide mich, den Trick von "-I" in ldLibs, wenn möglich, nicht zu verwenden. – weidongxu

+0

Wie untersucht/lädt man die hier aufgelisteten Dateien ?: https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA – Cliff

+0

Das Beispiel-Download gefunden, aber jetzt habe ich ein anderes Problem mit Gradle 1.11. Die generierte Datei "Androud.mk" verwendet absolute Pfade: http://stackoverflow.com/questions/23344567/ndk-dev-in-android-library-project-with-gradle-android-studio – Cliff

1

Ich verbrachte viel Zeit, um ndk in build.gradle zu konfigurieren. Ich habe eine good blog Lösung meines Problems.

0

Android Studio schlägt den Pfad schließen in local.properties

Verwandte Themen