Zoeken…


parameters

Parameter Details
JNIEnv Wijzer naar de JNI-omgeving
jobject Het object dat de niet- static native methode heeft aangeroepen
jclass De klasse die de static native methode heeft aangeroepen

Opmerkingen

Het instellen van JNI vereist zowel een Java- als een native compiler. Afhankelijk van de IDE en het besturingssysteem is een aantal instellingen vereist. Een gids voor Eclipse kan worden gevonden hier . Een volledige handleiding is te vinden hier .

Dit zijn de stappen voor het instellen van de Java-C ++ koppeling op Windows:

  • Compileer de Java-bronbestanden ( .java ) in klassen ( .class ) met behulp van javac .
  • Maak header ( .h ) bestanden van de Java-klassen met native methoden waarbij javah . Deze bestanden "instrueren" de native code welke methoden verantwoordelijk zijn voor de implementatie.
  • Neem de header-bestanden ( #include ) op in de C ++ bronbestanden ( .cpp ) die de native methoden implementeren.
  • Compileer de C ++ bronbestanden en maak een bibliotheek ( .dll ). Deze bibliotheek bevat de implementatie van de native code.
  • Geef het bibliotheekpad op ( -Djava.library.path ) en laad het in het Java-bronbestand ( System.loadLibrary(...) ).

Voor callbacks (oproepen van Java-methoden vanuit native code) moet een methodebeschrijving worden opgegeven. Als de descriptor onjuist is, treedt een runtime-fout op. Daarom is het handig om de descriptors voor ons te laten maken, dit kan met javap -s .

C ++ methoden aanroepen vanuit Java

Statische en lidmethoden in Java kunnen worden gemarkeerd als native om aan te geven dat hun implementatie te vinden is in een gedeeld bibliotheekbestand. Bij uitvoering van een native methode zoekt de JVM naar een overeenkomstige functie in geladen bibliotheken (zie Inheemse bibliotheken laden ), met behulp van een eenvoudig schema voor naammenging, voert argumentconversie en stapelconfiguratie uit en draagt vervolgens de controle over aan native 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

Koptekstbestanden met native functieverklaringen moeten worden gegenereerd met behulp van de javah tool op doelklassen. De volgende opdracht uitvoeren in de build-map:

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

... produceert het volgende koptekstbestand ( beknopt commentaar vanwege beknoptheid ):

// 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 is een voorbeeldimplementatie:

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

uitgang

Het uitvoeren van de bovenstaande voorbeeldklasse levert de volgende uitvoer op:

C ++ 'printString' aangeroepen vanuit Java
Kregen resultaat van C ++ 'gemiddeld': 3.4

Java-methoden oproepen vanuit C ++ (callback)

Het aanroepen van een Java-methode vanuit native code is een proces in twee stappen:

  1. verkrijg een methode pointer met de GetMethodID JNI functie, met behulp van de methode naam en descriptor;
  2. bel een van de hier genoemde Call*Method functies.

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);
}

uitgang

Gekregen float van C ++: 5.221
Got int van C ++: 17

De descriptor verkrijgen

Beschrijvingen (of interne typeaanduidingen ) worden verkregen met behulp van het javap- programma in het gecompileerde .class bestand. Hier is de uitvoer van 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
}

Inheemse bibliotheken laden

Het algemene idioom voor het laden van gedeelde bibliotheekbestanden in Java is het volgende:

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

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

Oproepen naar System.loadLibrary zijn bijna altijd statisch, zodat ze tijdens het laden van de klas kunnen voorkomen, zodat geen native methode kan worden uitgevoerd voordat de gedeelde bibliotheek is geladen. Het volgende is echter mogelijk:

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

    ...

Dit maakt het mogelijk het laden van gedeelde bibliotheken uit te stellen totdat dit nodig is, maar vereist extra zorg om java.lang.UnsatisfiedLinkError s te voorkomen.

Doelbestand opzoeken

Er wordt gezocht naar gedeelde bibliotheekbestanden in de paden die zijn gedefinieerd door de java.library.path , die tijdens runtime kunnen worden overschreven met het argument -Djava.library.path= JVM:

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

Pas op voor systeempadscheidingstekens: bijvoorbeeld Windows gebruikt ; in plaats van :

Merk op dat System.loadLibrary bibliotheekbestandsnamen op een platformafhankelijke manier oplost: het bovenstaande codefragment verwacht een bestand met de naam libExample.so op Linux en Example.dll op Windows.

Een alternatief voor System.loadLibrary is System.load(String) , die het volledige pad naar een gedeeld bibliotheekbestand neemt en de opzoeking java.library.path omzeilt:

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

    ...


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow