JNI Bagaimana memanggil java dan memanggil kembali native, Dan bagaimana mendapatkan JVM std io
Saya dev di windows tetapi tidak menggunakan windows lib.
Mode "inisiatif" JNI yang menjalankan java terlebih dahulu dan Systen.load()
kemudian memanggil metode native. Atau mode "pasif", Eksekusi buat JVM JNI_CreateJavaVM
lalu panggil metode java.
Sekarang, saya mencoba membuat program C ++ dengan JNI SDK, Jadi hal itu harus dapat dieksekusi tunggal, Tidak ada dll untuk System.load()
.
Pertama tulis hello world:
public static native int nativeSum(int a, int b);
public static int sum(int a, int b) {
return nativeSum(a, b);
}
dan jalankan javah Driver
header ini yang ditentukan
JNIEXPORT jint JNICALL Java_Driver_nativeSum (JNIEnv *, jclass, jint, jint);
dan jalankan javap -s Driver
pastikan menggunakan nama yang benar
public static native int nativeSum(int, int);
descriptor: (II)I
public static int sum(int, int);
descriptor: (II)I
tulis main.cpp
#include <iostream>
#include "jni.h"
#include "Driver.h"
JNIEXPORT jint JNICALL Java_Driver_nativeSum(JNIEnv*, jclass, jint a, jint b) {
std::cout << "Native invoked " << std::endl;
return a + b;
}
int main() {
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_8;
vm_args.ignoreUnrecognized = true;
vm_args.nOptions = 1;
auto* options = new JavaVMOption[1];
std::string cmd = "-Djava.class.path=../class/out/production/class";
options[0].optionString = const_cast<char*>(cmd.c_str());
vm_args.options = options;
JavaVM* jvm;
JNIEnv* env;
jint rc = JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);
delete[] options;
// ==========================================================
_jclass* jClass_Driver = env->FindClass("Driver");
_jmethodID* jMethod_Driver_sum = env->GetStaticMethodID(
jClass_Driver,
"sum",
"(II)I"
);
std::cout << "Test-sum method id = " << jMethod_Driver_sum << std::endl;
long jResult_Driver_sum = env->CallStaticIntMethod(
jClass_Driver,
jMethod_Driver_sum,
1, 1
);
std::cout << "Test-sum Method called res - "
<< jResult_Driver_sum
<< std::endl;
// ==========================================================
jvm->DestroyJavaVM();
return 0;
}
Hasil:
VM created
Test-sum method id = 0x1ebf4888
Test-sum Method called res - 0
Process finished with exit code 0
Nah, 1 + 1 = 0, Itu sama sekali tidak masuk akal.
Kemudian saya mencoba untuk menggunakan System.out/err
dan try catch
menemukan issusnya tetapi mendapatkan hasil yang sama, bahkan hal itu tidak dapat ditangkap oleh java exception atau bahkan C ++ try catch (...)
.
public static int sum(int a, int b) {
try {
return nativeSum(a, b);
} catch (Exception exception) {
return -1;
}
}
Kemudian pastikan tidak ada kesalahan lain, saya melewati native:
public static int sum(int a, int b) {
return 1234;
}
Bekerja dengan cukup baik, saya mendapatkan nilai 1234 di konsol C ++.
※ Pertanyaan pertama:
Bagaimana cara mendapatkan streaming JVM stdio? System.out/err.print
tidak akan ditampilkan di konsol "inisiatif". Tapi DLL std print akan mencetak di konsol java saat mode "pasif".
※ Pertanyaan kedua:
Apa yang terjadi dengan panggilan asli? Saya tidak mendapatkan hasil 0, Bagaimana cara memperbaikinya? Bagaimana cara mencapai tujuan tersebut?
BYW - tidak masuk akal tetapi percobaan yang bagus: menggunakan CallObjectMethod
akan mendapatkan hasil yang sama, menggunakan GetMethodID
akan mengembalikan ID 0 dan keluar macet lama dengan 1073741819 (0xC0000005).
Pembaruan 1:
jmethodID jMethod_Driver_nativeSum = env->GetStaticMethodID(
jClass_Driver,
"nativeSum",
"(II)I"
);
std::cout << jMethod_Driver_nativeSum << std::endl;
jint jResult_Driver_nativeSum = env->CallStaticIntMethod(
jClass_Driver,
jMethod_Driver_sum,
1, 1
);
std::cout << jResult_Driver_nativeSum << std::endl;
Punya keluaran ini
method id = 0x1ec97350
method result = 0
Perbarui 2:
Untuk memastikan tidak ekstern C atau lainya saya tulis saja function body di h
#include <jni.h>
/*
* Class: Driver
* Method: nativeSum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_Driver_nativeSum(JNIEnv* env, jclass cls, jint a, jint b) {
std::cout << "Java_Driver_nativeSum invoked" << std::endl;
return a + b;
}
Dan gunakan alat untuk memastikan nama fungsi sudah benar

Dan gunakan env->ExceptionOccurred
untuk mendapatkan Pengecualian, Dan ada satu:
java.lang.UnsatisfiedLinkError: Driver.nativeSum(II)I
Dan Tak satu pun dari dengan atau tanpa exter "C" {}
blok bekerja, Semua gagal sebagai hasil 0 dan UnsatisfiedLinkError
.
Jadi, saya pikir bahkan fungsi yang diperlukan asli dalam file exe, jvm tidak dapat menemukannya.
Sekarang situasinya adalah:
Program C ++ saya adalah main
entri, Dan tulis java SDK untuk pengembang plugin.
Dalam runtime, C ++ membuat JVM, Memuat kelas java, Memanggil metode java saat acara, Dan plugin menggunakan native untuk "melakukan sesuatu", Jadi bagaimana caranya?
Dan saya juga mencoba
public static int sum(int a, int b) {
return a + b;
}
Saya mendapat 2, Yang berfungsi dengan baik. Satu-satunya masalah adalah panggilan java asli.
Jawaban
Untuk mengakses metode asli, Anda masih harus memanggil System.LoadLibrary()
. The spek menjelaskan bahwa Anda Driver.java harus melihat mengandung:
public class Driver {
static { System.loadLibrary("driver"); } // this name must be matched!
public static native int nativeSum(int a, int b);
public static int sum(int a, int b) {
return nativeSum(a, b);
}
}
dan di main.cpp Anda ,
extern "C" JNIEXPORT jint JNICALL Java_Driver_nativeSum(JNIEnv*, jclass, jint a, jint b) {
std::cout << "Native invoked " << std::endl;
return a + b;
}
extern "C" JNIEXPORT jint JNI_OnLoad_driver // this suffix must match the name used in Java
(JavaVM *vm, void *reserved) {
std::cout << "Native loaded" << std::endl;
return JNI_VERSION_1_8;
}
Pastikan linker menyimpan keduanya Java_Driver_nativeSum
dan JNI_OnLoad_driver
diekspor ke biner Anda.
Adapun pertanyaan pertama Anda, tidak ada aliran stdio JVM yang terpisah, Java membaca dari yang sama fd=0
dan menulis sama fd=1
seperti yang lainnya.