Suche…


Parameter

Parameter Einzelheiten
JNIEnv Zeiger auf die JNI-Umgebung
Jobobjekt Das Objekt, das die nicht static native Methode aufgerufen hat
jclass Die Klasse, die die static native Methode aufgerufen hat

Bemerkungen

Zum Einrichten von JNI sind sowohl ein Java-Compiler als auch ein systemeigener Compiler erforderlich. Je nach IDE und Betriebssystem sind einige Einstellungen erforderlich. Eine Anleitung für Eclipse finden Sie hier . Eine vollständige Anleitung finden Sie hier .

Dies sind die Schritte zum Einrichten der Java-C ++ - Verknüpfung unter Windows:

  • Kompilieren Sie die Java-Quelldateien ( .java ) mit javac in Klassen ( .class ).
  • Erstellen Sie Header-Dateien ( .h ) aus den Java-Klassen, die native Methoden enthalten, mit javah . Diese Dateien "weisen" den nativen Code an, für welche Methoden er verantwortlich ist.
  • Fügen Sie die Header - Dateien ( #include ) in der C ++ Quelldateien ( .cpp ) die Umsetzung native Methoden.
  • Kompilieren Sie die C ++ - Quelldateien und erstellen Sie eine Bibliothek ( .dll ). Diese Bibliothek enthält die native Code-Implementierung.
  • Geben Sie den Bibliothekspfad ( -Djava.library.path ) an und laden Sie ihn in die Java-Quelldatei ( System.loadLibrary(...) ).

Callbacks (Aufruf von Java-Methoden aus nativem Code) erfordert die Angabe eines Methodendeskriptors. Wenn der Deskriptor falsch ist, tritt ein Laufzeitfehler auf. Aus diesem Grund ist es hilfreich, die Deskriptoren für uns erstellen zu javap -s . Dies kann mit javap -s .

C ++ - Methoden von Java aus aufrufen

Statische und Member-Methoden in Java können als native markiert werden , um anzuzeigen, dass ihre Implementierung in einer gemeinsam genutzten Bibliotheksdatei zu finden ist. Bei der Ausführung einer systemeigenen Methode sucht die JVM nach einer entsprechenden Funktion in geladenen Bibliotheken (siehe Laden von systemeigenen Bibliotheken ), verwendet ein einfaches Namensveränderungsschema, führt die Argumentkonvertierung und das Stack-Setup durch und übergibt die Kontrolle an den nativen Code.

Java-Code

/*** com/example/jni/JNIJava.java **/

package com.example.jni;

public class JNIJava {
    static {
        System.loadLibrary("libJNI_CPP");
    }

    // Obviously, native methods may not have a body defined in Java
    public native void printString(String name);
    public static native double average(int[] nums);

    public static void main(final String[] args) {
        JNIJava jniJava = new JNIJava();
        jniJava.printString("Invoked C++ 'printString' from Java");

        double d = average(new int[]{1, 2, 3, 4, 7});
        System.out.println("Got result from C++ 'average': " + d);
    }
}

C ++ - Code

Header-Dateien, die native Funktionsdeklarationen enthalten, sollten mit dem javah Tool für javah generiert werden. Führen Sie den folgenden Befehl im Build-Verzeichnis aus:

javah -o com_example_jni_JNIJava.hpp com.example.jni.JNIJava

... erzeugt die folgende Header-Datei (aus Gründen der Kürze entfernte Kommentare ):

// com_example_jni_JNIJava.hpp

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> // The JNI API declarations

#ifndef _Included_com_example_jni_JNIJava
#define _Included_com_example_jni_JNIJava
#ifdef __cplusplus
extern "C" { // This is absolutely required if using a C++ compiler
#endif

JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString
  (JNIEnv *, jobject, jstring);

JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average
  (JNIEnv *, jclass, jintArray);

#ifdef __cplusplus
}
#endif
#endif

Hier ist eine Beispielimplementierung:

// com_example_jni_JNIJava.cpp

#include <iostream>
#include "com_example_jni_JNIJava.hpp"

using namespace std;

JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString(JNIEnv *env, jobject jthis, jstring string) {
    const char *stringInC = env->GetStringUTFChars(string, NULL);
    if (NULL == stringInC)
        return;
    cout << stringInC << endl;
    env->ReleaseStringUTFChars(string, stringInC);
}

JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average(JNIEnv *env, jclass jthis, jintArray intArray) {
    jint *intArrayInC = env->GetIntArrayElements(intArray, NULL);
    if (NULL == intArrayInC)
        return -1;
    jsize length = env->GetArrayLength(intArray);
    int sum = 0;
    for (int i = 0; i < length; i++) {
        sum += intArrayInC[i];
    }
    env->ReleaseIntArrayElements(intArray, intArrayInC, 0);
    return (double) sum / length;
}

Ausgabe

Wenn Sie die obige Beispielklasse ausführen, erhalten Sie die folgende Ausgabe:

C ++ 'printString' von Java aufgerufen
Ergebnis aus C ++ "Durchschnitt": 3.4

Java-Methoden von C ++ aus aufrufen (Callback)

Das Aufrufen einer Java-Methode aus nativem Code erfolgt in zwei Schritten:

  1. einen Methodenzeiger mit der GetMethodID JNI-Funktion erhalten, wobei der Methodenname und der Deskriptor verwendet werden;
  2. Rufen Sie eine der hier aufgeführten Call*Method Funktionen auf.

Java-Code

/*** com.example.jni.JNIJavaCallback.java ***/

package com.example.jni;

public class JNIJavaCallback {
    static {
        System.loadLibrary("libJNI_CPP");
    }
    
    public static void main(String[] args) {
        new JNIJavaCallback().callback();
    }

    public native void callback(); 

    public static void printNum(int i) {
        System.out.println("Got int from C++: " + i);
    }
    
    public void printFloat(float i) {
        System.out.println("Got float from C++: " + i);
    }
}

C ++ - Code

// com_example_jni_JNICppCallback.cpp

#include <iostream>
#include "com_example_jni_JNIJavaCallback.h"

using namespace std;

JNIEXPORT void JNICALL Java_com_example_jni_JNIJavaCallback_callback(JNIEnv *env, jobject jthis) {
    jclass thisClass = env->GetObjectClass(jthis);

    jmethodID printFloat = env->GetMethodID(thisClass, "printFloat", "(F)V");
    if (NULL == printFloat)
        return;
    env->CallVoidMethod(jthis, printFloat, 5.221);

    jmethodID staticPrintInt = env->GetStaticMethodID(thisClass, "printNum", "(I)V");
    if (NULL == staticPrintInt)
        return;
    env->CallVoidMethod(jthis, staticPrintInt, 17);
}

Ausgabe

Erhielt Float aus C ++: 5.221
Bekam Int von C ++: 17

Den Deskriptor bekommen

Deskriptoren (oder interne Typunterschriften ) werden mit dem javap- Programm der kompilierten .class Datei abgerufen. Hier ist die Ausgabe von javap -p -s com.example.jni.JNIJavaCallback :

Compiled from "JNIJavaCallback.java"
public class com.example.jni.JNIJavaCallback {
  static {};
    descriptor: ()V

  public com.example.jni.JNIJavaCallback();
    descriptor: ()V

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V

  public native void callback();
    descriptor: ()V

  public static void printNum(int);
    descriptor: (I)V  // <---- Needed

  public void printFloat(float);
    descriptor: (F)V  // <---- Needed
}

Laden nativer Bibliotheken

Das übliche Idiom zum Laden von gemeinsam genutzten Bibliotheksdateien in Java lautet wie folgt:

public class ClassWithNativeMethods {
    static {
        System.loadLibrary("Example");
    }

    public native void someNativeMethod(String arg);
    ...

Aufrufe von System.loadLibrary sind fast immer statisch, sodass sie beim Laden der Klasse auftreten. System.loadLibrary wird sichergestellt, dass keine native Methode ausgeführt werden kann, bevor die gemeinsam genutzte Bibliothek geladen wurde. Folgendes ist jedoch möglich:

public class ClassWithNativeMethods {
    // Call this before using any native method
    public static void prepareNativeMethods() {
        System.loadLibrary("Example");
    }

    ...

Dies ermöglicht, das Laden der gemeinsam genutzten Bibliothek zu verschieben, bis dies erforderlich ist, erfordert jedoch besondere Vorsicht, um java.lang.UnsatisfiedLinkError s zu vermeiden.

Zieldatei-Lookup

java.library.path gemeinsam genutzten Bibliotheksdateien wird in den Pfaden gesucht, die in der java.library.path definiert sind. java.library.path können mit dem Argument -Djava.library.path= JVM zur Laufzeit überschrieben werden:

java -Djava.library.path=path/to/lib/:path/to/other/lib MainClassWithNativeMethods

Achten Sie auf Systempfadtrennzeichen: zum Beispiel für Windows ; anstelle von :

Beachten Sie, dass System.loadLibrary Bibliotheksdateinamen System.loadLibrary auflöst: Der obige Code-Snippet erwartet unter Linux eine Datei namens libExample.so und Example.dll Windows Example.dll .

Eine Alternative zu System.loadLibrary ist System.load(String) , die den vollständigen Pfad zu einer gemeinsam genutzten Bibliotheksdatei verwendet und die Suche nach java.library.path umgeht:

public class ClassWithNativeMethods {
    static {
        System.load("/path/to/lib/libExample.so");
    }

    ...


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow