Android mit NDK unterstützt C / C ++ - Code und iOS mit Objective-C ++ unterstützt auch. Wie kann ich also Anwendungen mit nativem C / C ++ - Code schreiben, die von Android und iOS gemeinsam genutzt werden?
java
c++
java-native-interface
cross-platform
objective-c++
ademar111190
quelle
quelle
Antworten:
Aktualisieren.
Diese Antwort ist auch vier Jahre nach dem Schreiben sehr beliebt. In diesen vier Jahren haben sich viele Dinge geändert. Deshalb habe ich beschlossen, meine Antwort zu aktualisieren, um sie besser an unsere aktuelle Realität anzupassen. Die Antwortidee ändert sich nicht; Die Implementierung hat sich ein wenig geändert. Mein Englisch hat sich ebenfalls geändert, es hat sich stark verbessert, sodass die Antwort jetzt für alle verständlicher ist.
Bitte schauen Sie sich das Repo an, damit Sie den unten gezeigten Code herunterladen und ausführen können.
Die Antwort
Bevor ich den Code zeige, nehmen Sie bitte viel auf das folgende Diagramm.
Jedes Betriebssystem hat seine Benutzeroberfläche und Besonderheiten, daher beabsichtigen wir, diesbezüglich spezifischen Code auf jede Plattform zu schreiben. Mit anderen Worten, alle Logikcodes, Geschäftsregeln und Dinge, die gemeinsam genutzt werden können, möchten wir mit C ++ schreiben, damit wir für jede Plattform denselben Code kompilieren können.
Im Diagramm sehen Sie die C ++ - Ebene auf der untersten Ebene. Der gesamte gemeinsam genutzte Code befindet sich in diesem Segment. Die höchste Ebene ist regulärer Obj-C / Java / Kotlin-Code, keine Neuigkeiten hier, der schwierige Teil ist die mittlere Ebene.
Die mittlere Ebene zur iOS-Seite ist einfach; Sie müssen Ihr Projekt nur so konfigurieren, dass es mit einer Variante von Obj-c namens erstellt wird Objective-C ++ bekannt ist, und Sie haben Zugriff auf C ++ - Code.
Auf der Android-Seite wurde es schwieriger, beide Sprachen, Java und Kotlin, auf Android, laufen unter einer Java Virtual Machine. Der einzige Weg, um auf C ++ - Code zuzugreifen, ist die Verwendung von JNI . Nehmen Sie sich bitte Zeit, um die Grundlagen von JNI zu lesen. Glücklicherweise weist die heutige Android Studio-IDE auf der JNI-Seite enorme Verbesserungen auf, und Ihnen werden beim Bearbeiten Ihres Codes viele Probleme angezeigt.
Der Code schrittweise
Unser Beispiel ist eine einfache App, mit der Sie einen Text an CPP senden. Dieser konvertiert diesen Text in etwas anderes und gibt ihn zurück. Die Idee ist, iOS sendet "Obj-C" und Android sendet "Java" aus ihren jeweiligen Sprachen, und der CPP-Code erstellt einen Text wie folgt "cpp sagt Hallo zu << empfangenem Text >> ".
Gemeinsamer CPP-Code
Zunächst erstellen wir den gemeinsam genutzten CPP-Code. Dabei haben wir eine einfache Header-Datei mit der Methodendeklaration, die den gewünschten Text erhält:
Und die CPP-Implementierung:
Unix
Ein interessanter Bonus ist, dass wir denselben Code auch für Linux und Mac sowie für andere Unix-Systeme verwenden können. Diese Möglichkeit ist besonders nützlich, da wir unseren freigegebenen Code schneller testen können. Daher erstellen wir eine Main.cpp wie folgt, um sie von unserem Computer aus auszuführen und zu überprüfen, ob der freigegebene Code funktioniert.
Um den Code zu erstellen, müssen Sie Folgendes ausführen:
iOS
Es ist Zeit, auf der mobilen Seite zu implementieren. Soweit iOS eine einfache Integration hat, beginnen wir damit. Unsere iOS-App ist eine typische Obj-c-App mit nur einem Unterschied. Die Dateien sind
.mm
und nicht.m
. dh es ist eine Obj-C ++ - App, keine Obj-C-App.Für eine bessere Organisation erstellen wir CoreWrapper.mm wie folgt:
Diese Klasse hat die Verantwortung, CPP-Typen und -Aufrufe in Obj-C-Typen und -Aufrufe zu konvertieren. Es ist nicht obligatorisch, wenn Sie CPP-Code für eine beliebige Datei auf Obj-C aufrufen können. Es hilft jedoch, die Organisation beizubehalten. Außerhalb Ihrer Wrapper-Dateien behalten Sie einen vollständigen Code im Obj-C-Stil bei. Nur die Wrapper-Datei wird im CPP-Stil erstellt .
Sobald Ihr Wrapper mit dem CPP-Code verbunden ist, können Sie ihn als Standard-Obj-C-Code verwenden, z. B. ViewController. "
Sehen Sie sich an, wie die App aussieht:
Android
Jetzt ist es Zeit für die Android-Integration. Android verwendet Gradle als Build-System und für C / C ++ - Code CMake. Als erstes müssen wir also die CMake-on-Gradle-Datei konfigurieren:
Der zweite Schritt ist das Hinzufügen der Datei CMakeLists.txt:
In der CMake-Datei müssen Sie die CPP-Dateien und Header-Ordner hinzufügen, die Sie für das Projekt verwenden. In unserem Beispiel fügen wir den
CPP
Ordner und die Core.h / .cpp-Dateien hinzu. Um mehr über die C / C ++ - Konfiguration zu erfahren, lesen Sie diese bitte .Jetzt ist der Kerncode Teil unserer App. Es ist Zeit, die Brücke zu erstellen. Um die Dinge einfacher und organisierter zu gestalten, erstellen wir eine bestimmte Klasse namens CoreWrapper, die unser Wrapper zwischen JVM und CPP ist:
Beachten Sie, dass diese Klasse eine
native
Methode hat und eine native Bibliothek mit dem Namen lädtnative-lib
. Diese Bibliothek ist diejenige, die wir erstellen. Am Ende wird der CPP-Code zu einer gemeinsam genutzten Objektdatei,.so
die in unsere APK eingebettet istloadLibrary
wird geladen. Wenn Sie die native Methode aufrufen, delegiert die JVM den Aufruf an die geladene Bibliothek.Der seltsamste Teil der Android-Integration ist jetzt das JNI. Wir benötigen eine cpp-Datei wie folgt, in unserem Fall "native-lib.cpp":
Das erste, was Sie bemerken werden, ist, dass
extern "C"
dieser Teil notwendig ist, damit JNI korrekt mit unseren CPP-Code- und Methodenverknüpfungen arbeitet. Sie werden auch einige Symbole sehen, die JNI verwendet, um mit JVM alsJNIEXPORT
und zu arbeitenJNICALL
. Um die Bedeutung dieser Dinge zu verstehen, ist es notwendig, sich eine Zeit zu nehmen und sie zu lesen . Für dieses Tutorial betrachten Sie diese Dinge einfach als Boilerplate.Eine wichtige Sache und normalerweise die Wurzel vieler Probleme ist der Name der Methode; Es muss dem Muster "Java_package_class_method" folgen. Derzeit bietet Android Studio eine hervorragende Unterstützung, sodass dieses Boilerplate automatisch generiert und Ihnen angezeigt werden kann, wenn es korrekt oder nicht benannt ist. In unserem Beispiel heißt unsere Methode "Java_ademar_androidioscppexample_CoreWrapper_concatenateMyStringWithCppString", weil "ademar.androidioscppexample" unser Paket ist, also ersetzen wir das "." Mit "_" ist CoreWrapper die Klasse, in der wir die native Methode verknüpfen, und "concatenateMyStringWithCppString" ist der Methodenname selbst.
Da wir die Methode korrekt deklariert haben, ist es Zeit, die Argumente zu analysieren. Der erste Parameter ist ein Zeiger
JNIEnv
darauf, wie wir auf JNI-Inhalte zugreifen können. Es ist entscheidend, dass wir unsere Konvertierungen durchführen, wie Sie gleich sehen werden. Das zweite istjobject
die Instanz des Objekts, mit dem Sie diese Methode aufgerufen haben. Sie können es als Java " dies " betrachten. In unserem Beispiel müssen wir es nicht verwenden, aber wir müssen es trotzdem deklarieren. Nach diesem Jobobjekt erhalten wir die Argumente der Methode. Da unsere Methode nur ein Argument hat - einen String "myString", haben wir nur einen "jstring" mit demselben Namen. Beachten Sie auch, dass unser Rückgabetyp auch ein Jstring ist. Da unsere Java-Methode einen String zurückgibt, lesen Sie ihn bitte, um weitere Informationen zu Java / JNI-Typen zu erhalten .Der letzte Schritt besteht darin, die JNI-Typen in die Typen zu konvertieren, die wir auf der CPP-Seite verwenden. In unserem Beispiel wandeln wir das
jstring
in einconst char *
Senden um, konvertieren es in das CPP, erhalten das Ergebnis und konvertieren zurück zujstring
. Wie bei allen anderen Schritten auf JNI ist es nicht schwer; es ist nur kesselplattiert, die ganze Arbeit wird durch dasJNIEnv*
Argument erledigt, das wir erhalten, wenn wir dasGetStringUTFChars
und aufrufenNewStringUTF
. Nachdem unser Code auf Android-Geräten ausgeführt werden kann, werfen wir einen Blick darauf.quelle
Der in der obigen hervorragenden Antwort beschriebene Ansatz kann von Scapix Language Bridge vollständig automatisiert werden, das Wrapper-Code im laufenden Betrieb direkt aus C ++ - Headern generiert. Hier ist ein Beispiel :
Definieren Sie Ihre Klasse in C ++:
Und nenne es von Swift:
Und aus Java:
quelle