Поиск…


параметры

параметр подробности
JNIEnv Указатель на среду JNI
jobject Объект , который ссылается на не- static native метода
JClass Класс, который вызвал static native метод

замечания

Для настройки JNI требуется как Java, так и собственный компилятор. В зависимости от IDE и ОС требуется некоторая настройка. Руководство для Eclipse можно найти здесь . Полный учебник можно найти здесь .

Это шаги для настройки связи Java-C ++ в окнах:

  • Скомпилируйте исходные файлы Java ( .java ) в классы ( .class ) с помощью javac .
  • Создайте файлы заголовка ( .h ) из классов Java, содержащих native методы, используя javah . Эти файлы «инструктируют» собственный код, какие методы он отвечает за реализацию.
  • Включите файлы заголовков ( #include ) в исходные файлы C ++ ( .cpp ), реализующие native методы.
  • Скомпилируйте исходные файлы C ++ и создайте библиотеку ( .dll ). Эта библиотека содержит реализацию собственного кода.
  • Укажите путь к библиотеке ( -Djava.library.path ) и загрузите его в исходный файл Java ( System.loadLibrary(...) ).

Обратные вызовы (вызов методов Java из собственного кода) требуют указания дескриптора метода. Если дескриптор неверен, возникает ошибка времени выполнения. Из-за этого полезно иметь дескрипторы для нас, это можно сделать с помощью javap -s .

Вызов методов C ++ из Java

Статические и членские методы в Java могут быть помечены как родные, чтобы указать, что их реализация должна быть найдена в файле общей библиотеки. После выполнения собственного метода JVM ищет соответствующую функцию в загружаемых библиотеках (см. Загрузка собственных библиотек ), используя простую схему переключения имен, выполняет преобразование аргументов и установку стека, а затем передает управление собственному коду.

Код 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);
    }
}

Код C ++

Заголовочные файлы, содержащие декларации javah функций, должны быть сгенерированы с использованием инструмента javah для целевых классов. Выполнение следующей команды в каталоге сборки:

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

... создает следующий заголовочный файл ( комментарии для краткости ):

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

Вот пример реализации:

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

Выход

Запуск класса example выше дает следующий результат:

Вызывается C ++ 'printString' из Java
Получил результат от C ++ «средний»: 3.4

Вызов методов Java из C ++ (callback)

Вызов метода Java из собственного кода - это двухэтапный процесс:

  1. получить указатель метода с GetMethodID , используя имя и дескриптор метода;
  2. вызовите одну из функций Call*Method перечисленных здесь .

Код 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);
    }
}

Код 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);
}

Выход

Поплавок с C ++: 5.221
Получил int из C ++: 17

Получение дескриптора

Дескрипторы (или сигнатуры внутреннего типа ) получают с помощью javap- программы в скомпилированном файле .class . Вот результат работы 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
}

Загрузка собственных библиотек

Общая идиома для загрузки файлов общей библиотеки в Java:

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

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

Вызовы в System.loadLibrary почти всегда статичны, чтобы происходить во время загрузки класса, гарантируя, что собственный метод не может быть запущен до того, как загружена общая библиотека. Однако возможно следующее:

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

    ...

Это позволяет отложить загрузку общей библиотеки до тех пор, пока это не будет необходимо, но требует дополнительной осторожности, чтобы избежать java.lang.UnsatisfiedLinkError s.

Поиск целевого файла

Поиск общих файлов библиотеки осуществляется в путях, определенных системным свойством java.library.path , которые могут быть переопределены с помощью аргумента -Djava.library.path= JVM во время выполнения:

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

Следите за разделителями системных путей: например, Windows использует ; а не :

Обратите внимание, что System.loadLibrary разрешает имена файлов библиотек зависимым от платформы образом: фрагмент кода выше ожидает файл с именем libExample.so в Linux и Example.dll в Windows.

Альтернативой System.loadLibrary является System.load(String) , которая переносит полный путь к файлу общей библиотеки, обходя поиск в 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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow