无法从JNI中的C ++包装器调用Java方法 [英] Can't call java methods from c++ wrapper in JNI

查看:58
本文介绍了无法从JNI中的C ++包装器调用Java方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是JNI和c ++的新手.我有一些需要与某些处理程序共享指针才能订阅某些消息的api.我可以在我的处理程序中的"main"中调用所需的方法. c ++方法,但是当我从c ++包装器调用它时,出现JVM错误,并且我的应用程序崩溃.接下来是我的本机方法:

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处理程序类:

Java Handler 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类编写了c ++包装器,以将其传递给订阅方法:

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

};
};

主题调用调用"方法时,我收到JVM错误:

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

Java运行时环境检测到致命错误:

A fatal error has been detected by the Java Runtime Environment:

SIGSEGV(0xb)at pc = 0x7694d8a4,pid = 5681,tid = 5702

SIGSEGV (0xb) at pc=0x7694d8a4, pid=5681, tid=5702

JRE版本:OpenJDK运行时环境(Zulu11.31 + 16-CA)(11.0.3 + 7)(内部版本11.0.3 + 7-LTS) Java VM:OpenJDK客户端VM(11.0.3 + 7-LTS,混合模式,串行gc,linux-arm) 有问题的框架: V [libjvm.so + 0x3e58a4] get_method_id(JNIEnv_ ,_jclass ,char const *,char const *,bool,Thread *)>> [clone .isra.149] + 0x288

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

怎么了?预先感谢.

推荐答案

最后,我编写了工作代码.在本机方法中,当需要在另一个线程中使用它时,需要检索并保存JVM变量(可以在线程之间共享)以检索JNIenv(不能在线程之间共享):

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

然后,在需要的位置检索env.还需要将当前线程附加到vm:

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

};
};

这篇关于无法从JNI中的C ++包装器调用Java方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆