Szukaj…


Parametry

Parametr Detale
JNIEnv Wskaźnik do środowiska JNI
Jobject Obiekt, który wywołał static metodę native
jclass Klasa, która wywołała static metodę native

Uwagi

Konfigurowanie JNI wymaga zarówno Java, jak i rodzimego kompilatora. W zależności od IDE i systemu operacyjnego wymagana jest pewna konfiguracja. Przewodnik po Eclipse można znaleźć tutaj . Pełny samouczek można znaleźć tutaj .

Oto kroki konfigurowania połączenia Java-C ++ w systemie Windows:

  • Skompiluj pliki źródłowe Java ( .java ) w klasy ( .class ) przy użyciu javac .
  • Utwórz pliki nagłówka ( .h ) z klas Java zawierających metody native przy użyciu javah . Pliki te „instruują” natywny kod, które metody są odpowiedzialne za implementację.
  • Dołącz pliki nagłówkowe ( #include ) do plików źródłowych C ++ ( .cpp ) implementujących metody native .
  • Skompiluj pliki źródłowe C ++ i utwórz bibliotekę ( .dll ). Ta biblioteka zawiera natywną implementację kodu.
  • Podaj ścieżkę do biblioteki ( -Djava.library.path ) i załaduj ją do pliku źródłowego Java ( System.loadLibrary(...) ).

Oddzwanianie (wywoływanie metod Java z kodu macierzystego) wymaga określenia deskryptora metody. Jeśli deskryptor jest niepoprawny, występuje błąd czasu wykonywania. Z tego powodu pomocne jest stworzenie dla nas deskryptorów, można to zrobić za pomocą javap -s .

Wywoływanie metod C ++ z Java

Metody statyczne i składowe w Javie można oznaczyć jako rodzime, aby wskazać, że ich implementacja znajduje się we wspólnym pliku biblioteki. Po wykonaniu metody natywnej JVM szuka odpowiedniej funkcji w załadowanych bibliotekach (patrz Ładowanie bibliotek natywnych ), używając prostego schematu manglingu nazw, wykonuje konwersję argumentów i konfigurację stosu, a następnie przekazuje kontrolę nad kodem natywnym.

Kod Java

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

Kod C ++

Pliki nagłówkowe zawierające deklaracje funkcji rodzimych powinny być generowane przy użyciu narzędzia javah dla klas docelowych. Uruchom następujące polecenie w katalogu kompilacji:

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

... tworzy następujący plik nagłówka ( komentarze są usuwane ze względu na zwięzłość ):

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

Oto przykładowa implementacja:

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

Wynik

Uruchomienie powyższej klasy przykładowej daje następujące wyniki:

Wywołano C ++ „printString” z Java
Otrzymałem wynik z „średniej” C ++: 3.4

Wywoływanie metod Java z C ++ (callback)

Wywołanie metody Java z kodu natywnego jest procesem dwuetapowym:

  1. uzyskać wskaźnik metody za pomocą funkcji GetMethodID JNI, używając nazwy metody i deskryptora;
  2. wywołaj jedną z funkcji Call*Method wymienionych tutaj .

Kod Java

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

Kod C ++

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

Wynik

Mam zmiennoprzecinkowe z C ++: 5.221
Mam int z C ++: 17

Uzyskiwanie deskryptora

Deskryptory (lub podpisy typu wewnętrznego ) są uzyskiwane za pomocą programu javap w skompilowanym pliku .class . Oto dane wyjściowe 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
}

Ładowanie rodzimych bibliotek

Typowy idiom do ładowania plików bibliotek współdzielonych w Javie jest następujący:

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

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

Wywołania System.loadLibrary są prawie zawsze statyczne, aby występowały podczas ładowania klas, zapewniając, że żadna metoda natywna nie będzie mogła zostać wykonana przed załadowaniem biblioteki współdzielonej. Możliwe są jednak:

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

    ...

Pozwala to odroczyć ładowanie biblioteki współużytkowanej, dopóki nie będzie to konieczne, ale wymaga dodatkowej uwagi, aby uniknąć java.lang.UnsatisfiedLinkError .

Wyszukiwanie pliku docelowego

Pliki bibliotek współużytkowanych są wyszukiwane w ścieżkach zdefiniowanych we właściwości systemowej java.library.path , które można zastąpić za pomocą argumentu -Djava.library.path= JVM w czasie wykonywania:

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

Uważaj na separatory ścieżek systemowych: na przykład Windows używa ; zamiast : .

Zauważ, że System.loadLibrary rozwiązuje nazwy plików bibliotecznych w sposób zależny od platformy: urywek kodu powyżej spodziewa się plik o nazwie libExample.so na Linuksie, a Example.dll na Windows.

Alternatywą dla System.loadLibrary jest System.load(String) , który pobiera pełną ścieżkę do pliku biblioteki współużytkowanej, omijając wyszukiwanie java.library.path :

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

    ...


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow