无法从 JNI 中的 C++ 包装器调用 Java 方法

问题描述 投票:0回答:2

我是 JNI 和 C++ 新手。我有一些 API 需要与某些处理程序共享指针来订阅某些消息。我可以在“主”C++ 方法中调用处理程序中所需的方法,但是当我从 C++ 包装器调用它时,我收到 JVM 错误并且应用程序崩溃。我的本机方法是下一个:

public native int subscribe(Handler handler);

Java 处理程序类:

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++ 包装器,将其传递给订阅方法:

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

};
};

当主题调用“call”方法时,我收到 JVM 错误:

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

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

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

出了什么问题?

java c++ pointers java-native-interface
2个回答
1
投票

只是要扩展@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 定义中的三规则,但您可以跳过修复该问题(因为使用全局引用修复它并不简单),只要您确保您的对象永远不会出现,除非通过指针引用。


0
投票

最后,我编写了工作代码。在本机方法中,需要检索并保存 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);
};

然后,在需要的地方检索 env。它还需要将当前线程附加到虚拟机:

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

};
};
© www.soinside.com 2019 - 2024. All rights reserved.