无法从 JNI 中的 C++ 包装器调用 java 方法
Can't call java methods from c++ wrapper in JNI
我是 JNI 和 C++ 的新手。我有一些 api 需要与某些处理程序共享指针来订阅某些消息。我可以在“main”c++ 方法中调用我的处理程序中的必需方法,但是当我从 c++ 包装器调用它时,我得到 JVM 错误并且我的应用程序崩溃。接下来是我的原生方法:
public native int subscribe(Handler handler);
Java 处理程序 class:
public class Handler {
public void call(String m1, String m2) {
System.out.println("call: " + m1 + " " + m2);
}
}
JNI 实现:
JNIEXPORT jint JNICALL Java_com_lib_NativeClient_subscribe (JNIEnv* env, jobject thisObj, jobject javaHandler) {
jclass handlerClass = env->GetObjectClass(javaHandler);
jmethodID call = env->GetMethodID(handlerClass, "call", "(Ljava/lang/String;Ljava/lang/String;)V");
const std::string &message1 = "message1";
const std::string &message2 = "message2";
jstring javMessage1 = env->NewStringUTF((const char* )message1.c_str());
jstring javMessage2 = env->NewStrbingUTF((const char* )message2.c_str());
env->CallVoidMethod(javaHandler, call, javMessage1, javMessage2);
JavaWrapperHandler javaWrapperHandler = JavaWrapperHandler(env, javaHandler);
std::shared_ptr<JavaWrapperHandler> handlerSharedPointer = std::make_shared<JavaWrapperHandler>(javaWrapperHandler);
return some::lib::subscribe(handlerSharedPointer);
};
一切正常,我用这段代码调用了 'call' 方法。但是我需要在订阅消息后调用这个方法,即主体会调用它。我为我的 java class 编写了 c++ 包装器以将其传递给订阅方法:
class JavaWrapperHandler : public some::lib::Handler {
JNIEnv* env;
jobject javaHandler;
public:
JavaWrapperHandler(JNIEnv* genEnv, jobject handler) {
env = genEnv;
javaHandler = env->NewGlobalRef(handler);
}
~JavaWrapperHandler() {
env->DeleteGlobalRef(javaHandler);
}
virtual void call(const std::string &message1, const std::string &message2) {
jclass handlerClass = env->GetObjectClass(javaHandler);
jmethodID call = env->GetMethodID(handlerClass, "call", "(Ljava/lang/String;Ljava/lang/String;)V"); // Here I get error
jstring javMessage1 = env->NewStringUTF((const char* )message1.c_str());
jstring javMessage2 = env->NewStringUTF((const char* )message2.c_str());
env->CallVoidMethod(javaHandler, call, javMessage1, javMessage2);
};
};
当 Subject 调用 'call' 方法时,我收到 JVM 错误:
A fatal error has been detected by the Java Runtime Environment:
SIGSEGV (0xb) at pc=0x7694d8a4, pid=5681, tid=5702
JRE version: OpenJDK Runtime Environment (Zulu11.31+16-CA) (11.0.3+7) (build 11.0.3+7-LTS)
Java VM: OpenJDK Client VM (11.0.3+7-LTS, mixed mode, serial gc, linux-arm)
Problematic frame:
V [libjvm.so+0x3e58a4] get_method_id(JNIEnv_, _jclass, char const*, char const*, bool, Thread*) >>[clone .isra.149]+0x288
怎么了?提前致谢。
只是想扩展@PaulMcKenzie 的评论。
您需要更换:
JavaWrapperHandler javaWrapperHandler = JavaWrapperHandler(env, javaHandler);
std::shared_ptr<JavaWrapperHandler> handlerSharedPointer = std::make_shared<JavaWrapperHandler>(javaWrapperHandler);
和
std::shared_ptr<JaveWrapperHandler> handlerSharedPointer = std::make_shared<JavaWrapperHandler>(env, javaHandler);
您违反了 JavaWrapperHandler 定义中的三个规则,但您可以跳过修复它(因为修复它不是通过全局引用直接进行的),只要您确保您的对象永远不会出现,除非通过指针引用。
最后,我编写了工作代码。在本机方法中,需要检索并保存 JVM 变量(可以在线程之间共享)以在另一个线程需要时检索 JNIenv(不能在线程之间共享):
JNIEXPORT jint JNICALL Java_com_lib_NativeClient_subscribe (JNIEnv* env, jobject thisObj, jobject javaHandler) {
static JavaVM *jvm;
int status = env->GetJavaVM(&jvm);
if(status != 0) {
std::cout << "Failed to receive JavaVm instance" << std::endl;
}
std::shared_ptr<JavaWrapperHandler> handlerSharedPointer =
std::make_shared<JavaWrapperHandler>(jvm, javaWrapperHandler);
return some::lib::subscribe(handlerSharedPointer);
};
然后,在需要的地方检索环境。它还需要将当前线程附加到 vm:
class JavaWrapperHandler : public some::lib::Handler {
JavaVM *vm;
jobject javaHandler;
public:
JavaWrapperHandler(JavaVM *gen_vm, jobject handler) {
vm = gen_jvm;
JNIEnv *env = nullptr;
vm->GetEnv((void**)&env, JNI_VERSION_1_6);
javaHandler = env->NewGlobalRef(handler);
}
~JavaWrapperHandler() {}
virtual void call(const std::string &message1, const std::string &message2) {
JNIEnv *env = nullptr;
auto result = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
if (result == JNI_EDETACHED) {
std::cout << "Thread detached." << std::endl;
if (vm->AttachCurrentThread((void**)&env, NULL) == JNI_OK) {
std::cout << "Attach current thread to vm" << std::endl;
} else {
std::cout << "Failed to attach thread." << std::endl;
}
} else if (result == JNI_EVERSION) {
std::cout << "Unsupported JNI version." << std::endl;
}
jclass handlerClass = env->GetObjectClass(javaHandler);
jmethodID call = env->GetMethodID(handlerClass, "call", "(Ljava/lang/String;Ljava/lang/String;)V"); // Here I get error
jstring javMessage1 = env->NewStringUTF((const char* )message1.c_str());
jstring javMessage2 = env->NewStringUTF((const char* )message2.c_str());
env->CallVoidMethod(javaHandler, call, javMessage1, javMessage2);
};
};
我是 JNI 和 C++ 的新手。我有一些 api 需要与某些处理程序共享指针来订阅某些消息。我可以在“main”c++ 方法中调用我的处理程序中的必需方法,但是当我从 c++ 包装器调用它时,我得到 JVM 错误并且我的应用程序崩溃。接下来是我的原生方法:
public native int subscribe(Handler handler);
Java 处理程序 class:
public class Handler {
public void call(String m1, String m2) {
System.out.println("call: " + m1 + " " + m2);
}
}
JNI 实现:
JNIEXPORT jint JNICALL Java_com_lib_NativeClient_subscribe (JNIEnv* env, jobject thisObj, jobject javaHandler) {
jclass handlerClass = env->GetObjectClass(javaHandler);
jmethodID call = env->GetMethodID(handlerClass, "call", "(Ljava/lang/String;Ljava/lang/String;)V");
const std::string &message1 = "message1";
const std::string &message2 = "message2";
jstring javMessage1 = env->NewStringUTF((const char* )message1.c_str());
jstring javMessage2 = env->NewStrbingUTF((const char* )message2.c_str());
env->CallVoidMethod(javaHandler, call, javMessage1, javMessage2);
JavaWrapperHandler javaWrapperHandler = JavaWrapperHandler(env, javaHandler);
std::shared_ptr<JavaWrapperHandler> handlerSharedPointer = std::make_shared<JavaWrapperHandler>(javaWrapperHandler);
return some::lib::subscribe(handlerSharedPointer);
};
一切正常,我用这段代码调用了 'call' 方法。但是我需要在订阅消息后调用这个方法,即主体会调用它。我为我的 java class 编写了 c++ 包装器以将其传递给订阅方法:
class JavaWrapperHandler : public some::lib::Handler {
JNIEnv* env;
jobject javaHandler;
public:
JavaWrapperHandler(JNIEnv* genEnv, jobject handler) {
env = genEnv;
javaHandler = env->NewGlobalRef(handler);
}
~JavaWrapperHandler() {
env->DeleteGlobalRef(javaHandler);
}
virtual void call(const std::string &message1, const std::string &message2) {
jclass handlerClass = env->GetObjectClass(javaHandler);
jmethodID call = env->GetMethodID(handlerClass, "call", "(Ljava/lang/String;Ljava/lang/String;)V"); // Here I get error
jstring javMessage1 = env->NewStringUTF((const char* )message1.c_str());
jstring javMessage2 = env->NewStringUTF((const char* )message2.c_str());
env->CallVoidMethod(javaHandler, call, javMessage1, javMessage2);
};
};
当 Subject 调用 'call' 方法时,我收到 JVM 错误:
A fatal error has been detected by the Java Runtime Environment:
SIGSEGV (0xb) at pc=0x7694d8a4, pid=5681, tid=5702
JRE version: OpenJDK Runtime Environment (Zulu11.31+16-CA) (11.0.3+7) (build 11.0.3+7-LTS) Java VM: OpenJDK Client VM (11.0.3+7-LTS, mixed mode, serial gc, linux-arm) Problematic frame: V [libjvm.so+0x3e58a4] get_method_id(JNIEnv_, _jclass, char const*, char const*, bool, Thread*) >>[clone .isra.149]+0x288
怎么了?提前致谢。
只是想扩展@PaulMcKenzie 的评论。
您需要更换:
JavaWrapperHandler javaWrapperHandler = JavaWrapperHandler(env, javaHandler);
std::shared_ptr<JavaWrapperHandler> handlerSharedPointer = std::make_shared<JavaWrapperHandler>(javaWrapperHandler);
和
std::shared_ptr<JaveWrapperHandler> handlerSharedPointer = std::make_shared<JavaWrapperHandler>(env, javaHandler);
您违反了 JavaWrapperHandler 定义中的三个规则,但您可以跳过修复它(因为修复它不是通过全局引用直接进行的),只要您确保您的对象永远不会出现,除非通过指针引用。
最后,我编写了工作代码。在本机方法中,需要检索并保存 JVM 变量(可以在线程之间共享)以在另一个线程需要时检索 JNIenv(不能在线程之间共享):
JNIEXPORT jint JNICALL Java_com_lib_NativeClient_subscribe (JNIEnv* env, jobject thisObj, jobject javaHandler) {
static JavaVM *jvm;
int status = env->GetJavaVM(&jvm);
if(status != 0) {
std::cout << "Failed to receive JavaVm instance" << std::endl;
}
std::shared_ptr<JavaWrapperHandler> handlerSharedPointer =
std::make_shared<JavaWrapperHandler>(jvm, javaWrapperHandler);
return some::lib::subscribe(handlerSharedPointer);
};
然后,在需要的地方检索环境。它还需要将当前线程附加到 vm:
class JavaWrapperHandler : public some::lib::Handler {
JavaVM *vm;
jobject javaHandler;
public:
JavaWrapperHandler(JavaVM *gen_vm, jobject handler) {
vm = gen_jvm;
JNIEnv *env = nullptr;
vm->GetEnv((void**)&env, JNI_VERSION_1_6);
javaHandler = env->NewGlobalRef(handler);
}
~JavaWrapperHandler() {}
virtual void call(const std::string &message1, const std::string &message2) {
JNIEnv *env = nullptr;
auto result = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
if (result == JNI_EDETACHED) {
std::cout << "Thread detached." << std::endl;
if (vm->AttachCurrentThread((void**)&env, NULL) == JNI_OK) {
std::cout << "Attach current thread to vm" << std::endl;
} else {
std::cout << "Failed to attach thread." << std::endl;
}
} else if (result == JNI_EVERSION) {
std::cout << "Unsupported JNI version." << std::endl;
}
jclass handlerClass = env->GetObjectClass(javaHandler);
jmethodID call = env->GetMethodID(handlerClass, "call", "(Ljava/lang/String;Ljava/lang/String;)V"); // Here I get error
jstring javMessage1 = env->NewStringUTF((const char* )message1.c_str());
jstring javMessage2 = env->NewStringUTF((const char* )message2.c_str());
env->CallVoidMethod(javaHandler, call, javMessage1, javMessage2);
};
};