Can't call java methods from c++ wrapper in JNI

空扰寡人 提交于 2020-08-08 05:16:04

问题


I'm new in JNI and c++. I have some api that required shared pointer with some handler to subscribe on some messages. I can call required method in my handler in "main" c++ method, but when I call it from c++ wrapper I get JVM error and my application crash. My native method is next:

public native int subscribe(Handler handler);

Java Handler class:

public class Handler {
public void call(String m1, String m2) {
    System.out.println("call: " + m1 + " " + m2);
}

}

JNI implementation:

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

All works fine, I call 'call' method with this code. But I need to call this method after I subscribe to messages, I.e. Subject will call it. I write c++ wrapper for my java class to pass it to subscribe method:

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

};
};

When Subject call 'call' method I receive JVM error:

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

What is wrong? Thanks in advance.


回答1:


Just going to expand on @PaulMcKenzie 's comment.

You need to replace:

JavaWrapperHandler javaWrapperHandler = JavaWrapperHandler(env, javaHandler);
std::shared_ptr<JavaWrapperHandler> handlerSharedPointer = std::make_shared<JavaWrapperHandler>(javaWrapperHandler);

with

std::shared_ptr<JaveWrapperHandler> handlerSharedPointer = std::make_shared<JavaWrapperHandler>(env, javaHandler);

You are violating the rule of three in your definition of JavaWrapperHandler, but you can skip fixing that (since fixing it isn't straightforward with the global reference) as long as you make sure your object never appears except through a pointer reference.




回答2:


Finally, I wrote working code. In the native method it is required to retrieve and save JVM variable (which can be shared between threads) to retrieve JNIenv (which can't be shared between threads) when it will required in another thread:

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

Then, retrieve env where it is required. Also it required to attach current thread to 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);

};
};


来源:https://stackoverflow.com/questions/63016784/cant-call-java-methods-from-c-wrapper-in-jni

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!