Ich beginne gerade mit JNI und habe ein folgendes Problem.
Ich habe eine C ++ - Bibliothek, die eine einfache Klasse hat. Ich habe drei JNI-Methoden, die aus dem Java-Android-Projekt aufgerufen wurden, die diese Klasse instatieren, eine Methode für die instanziierte Klasse aufrufen bzw. sie zerstören. Ich behalte einen globalen Verweis auf dieses Objekt, also wäre es für mich in den anderen beiden JNI-Methoden verfügbar.
Ich vermute, dass ich das nicht tun kann. Wenn ich die App ausführe, erhalte ich einen Laufzeitfehler (verwendeter Verweis abgestanden), und ich vermute, dass dies daran liegt, dass die globale Referenz bei nachfolgenden Aufrufen anderer JNI-Methoden ungültig ist.
Ist der einzige Weg, um das zu erreichen, was ich möchte (das Objekt über mehrere JNI-Aufrufe hinweg leben lassen), den Zeiger auf die instanziierte Klasse zurück an Java zu übergeben, dort herumzuhalten und dann an den JNI zu übergeben Funktionen? Wenn ja, ist das in Ordnung, ich möchte sicherstellen, dass ich es nicht mit einer globalen Referenz tun kann, und ich verpasse nicht einfach etwas.
Ich habe die Dokumentation und die Kapitel über globale / lokale Referenzen in JNI gelesen, aber es scheint, dass das nur für Java-Klassen gilt, und nicht für meine eigenen, nativen C ++ - Klassen, oder liege ich falsch.
Hier ist der Code, wenn meine Beschreibung nicht klar ist (zusammenfassend, ich frage mich, ob dieser Mechanismus von persistenten Objekten überhaupt funktioniert):
Java:
%Vor%}
JNI-Header:
%Vor%JNI Körper:
%Vor%C ++ Header:
%Vor%C ++ Körper:
%Vor%Danke
Das Problem mit Ihrer Implementierung ist das Datenelement jstring
. NewStringUTF()
erstellt ein Java String
-Objekt, das von einer JNI-Methode zurückgegeben werden soll. Es ist also eine Java-lokale Referenz. Sie speichern das jedoch in einem C ++ - Objekt und versuchen, es über JNI-Aufrufe hinweg zu verwenden.
Sie sollten besser zwischen C ++ - Objekten, Java und der dazwischen liegenden JNI-Schnittstelle unterscheiden. Mit anderen Worten, das C ++ sollte eine C ++ - Methode zum Speichern von Strings verwenden (wie std::string
). Die JNI-Implementierung von InvokeNativeFunction()
sollte das in einen jstring
als Rückgabewert konvertieren.
PS: Dort sind Fälle Fälle, in denen die C ++ - Implementierung Verweise auf Java-Objekte beibehalten muss (oder umgekehrt). Aber es macht den Code komplexer und anfällig für Speicherfehler, wenn nicht richtig gemacht. Sie sollten es also nur dort verwenden, wo es wirklich einen Mehrwert darstellt.
Ich konnte zu diesem Thema keine gute Antwort zu SO finden, also ist hier meine Lösung, um Objekte in C ++ lebendig zu halten, um sie aus mehreren JNI-Aufrufen zu referenzieren:
Java
Auf der Java-Seite erstelle ich eine Klasse mit einem long
-Zeiger, um einen Verweis auf das C ++ - Objekt zu behalten. Das Wrapping der C ++ - Methoden in einer Java-Klasse ermöglicht es uns, die C ++ - Methoden in mehreren Aktivitäten zu verwenden. Beachten Sie, dass ich das C ++ - Objekt für den Konstruktor erstelle und das Objekt beim Bereinigen löscht. Dies ist sehr wichtig, um Speicherlecks zu vermeiden:
C ++
Auf der C ++ - Seite definiere ich Funktionen zum Erstellen, Ändern und Löschen des Objekts. Es ist wichtig zu erwähnen, dass wir new
und delete
verwenden müssen, um das Objekt im HEAP-Speicher zu speichern, damit es während des gesamten Lebenszyklus der Java-Klasseninstanzen am Leben bleibt. Ich speichere auch den Zeiger auf CppObject
gerade in der JavaClass
mit getFieldId
, SetLongField
und GetLongField
:
HINWEISE:
delete
. GetFieldID
, SetLongField
und GetLongField
, um den Objektreferenz von C ++ zu speichern, aber Sie könnten auch den Objektzeiger jlong
aus Java speichern, wie in anderen Antworten besprochen. JavaObject
als Parcelable
implementiert, um meine Klasse an mehrere Aktivitäten zu übergeben, indem ich Intent
mit Extras verwende.