【问题标题】:calling java method from c++ using JNI使用 JNI 从 c++ 调用 java 方法
【发布时间】:2012-03-28 23:06:32
【问题描述】:

我正在尝试在 JAVA 中为特定的 USB 设备包装 C++ 库。 该库支持回调函数,以通知应用程序有关 USB 设备与 PC 的连接和分离。

回调函数必须具有如下特定格式:

DWORD callbackFunction(void *params);

所以我在 JNI dll 中实现了一个这样的函数,并且想在调用这个函数时调用 Java wapper 中的一个函数。

问题是我应该使用什么 JNIENV 来调用 GetObjectClass、GetMethodID 和 CallVoidMethod?


这就是我初始化 DLL 的方式。 “Set(AttachDetach)Callback”方法接受一个回调函数(第一个参数)和一个 void* 参数(第二个参数),当检测到模块附加/分离时,它们将传递给函数。

JNIEXPORT void JNICALL Java_MyPackage_MyClass_InitializeDLL
(JNIEnv *env, jobject obj, jobject callback)
{
      // Storing callback object in global variable.
    callBackObj = callback;

    env->GetJavaVM(&jvm);

    MyInstance = new MyClass();
    MyInstance ->SetAttachCallback(AttachCallBack, &callBackObj);
    MyInstance ->SetDetachCallback(DetachCallBack, &callBackObj);

      // Testing!
    jclass callBackCls = env->FindClass("MyPackage/MyClassCallBacks");
    jmethodID mid = env->GetMethodID(callBackCls, "attach", "(B)V");
    if (!mid)
        return ; /* method not found */
      //This call here works well
    env->CallVoidMethod(callBackObj, mid, 5);
}

然后我在 DLL 中为 USB 设备设置了一个回调函数,当我连接设备时它被成功调用。

我在 USB 设备的附加回调中的代码是这样的:

DWORD CALLBACK AttachCallBack(CallbackParams* params)
{
    JNIEnv *env;
    jvm->AttachCurrentThread((void **)&env, NULL);

    jclass callBackCls = env->FindClass("MyPackage/MyClassCallBacks");
    jmethodID mid = env->GetMethodID(callBackCls, "attach", "(B)V");
    if (!mid)
        return -1; /* method not found */
      // This call fails with an access violation Exception
    env->CallVoidMethod(*((jobject *)(params->param)), mid, params->moduleIndex);
      // This fails the same way too
    env->CallVoidMethod(callBackObj, mid, 5);

    jvm->DetachCurrentThread();

    return 0;
}

在我使用 AttachCurrentThread 之前,我根本无法使用 JNIENV 指针。但现在该指针的任何其他使用都是成功的,而不是调用 CallVoidMethod。 你知道这里有什么问题吗?

让我补充一下 MyPackage.MyClassCallBacks 是一个接口,它的方法在另一个类中实现,即“callBackClass”

【问题讨论】:

    标签: java c++ methods java-native-interface


    【解决方案1】:

    你需要对当前的JVM有一个引用:

    JavaVM *jvm;
    

    可以在C++后端添加一个初始化方法,在程序启动时获取这个引用:

    JNIEXPORT void JNICALL init(JNIEnv *env, jclass){
        env->GetJavaVM(&jvm);
    }
    

    当观察 USB 连接/分离时,您可以像这样从这个 JavaVM 获取 JNIEnv:

    JNIEnv *env;
    jvm->AttachCurrentThread((void **)&env, NULL);
    
    //your code here
    
    jvm->DetachCurrentThread();
    

    这是为了让每个 USB 设备更改都会创建一个新线程。如果您只使用一个线程进行检查,则只需附加一次(也许是在初始化程序中?),然后只要您的本机线程保持连接到 JVM,JNIEnv 就会有效。

    【讨论】:

    • 嗨,Jakub。谢谢你的回复。这解决了我的问题。但是现在我又遇到了另一个问题。我可以使用从 GetJavaVM 方法获得的 JNIENV 参数获取 jclass 和方法 ID,但是在 JAVA 中调用该函数(使用 CallVoidMethod)会导致访问冲突异常。我在本机库中为 USB 设备设置的回调函数在与主线程不同的新线程中调用。
    • 一般情况下,您想与 Java 一起使用的每个新线程都必须使用 AttachCurrentThread() 附加。它将使 JVM 将此本机线程与 Thread 对象相关联。如果您尝试从未附加的线程调用 Java 函数,则会出现访问冲突。发布更多代码,我们会看到。
    【解决方案2】:

    您可能需要做的是在 C 中创建一个队列并等待它或使用 Java 线程对其进行轮询。这将始终有一个可用的当前 JNIEnv。


    看来你不能...

    ....保存上次 JNI 调用中的 JNIENV 设置并重用它。

    您的回调似乎返回了您在设置回调时可能已传递给它的参数。您可以将其中之一设为 JNIENV。

    【讨论】:

    • 您通常应该保存 JNIEnv 以供以后重复使用,即使它偶尔会这样做。 Java 回调应该以 JVM 引用开始,它们可以从中获取适当的 JNIEnv 指针。
    • 你能解释一下这是如何做到的吗?
    • 第一次进入 JNI-land 时,保存了对 VM 的引用(请参阅 Jakub 的回答)。从 VM 参考中,您可以稍后检索特定于上下文的 JNIEnv 指针。
    • 是的,保存 JNI 并在以后使用它会导致访问冲突异常。
    【解决方案3】:

    我也有同样的问题。就好像在初始化方法中创建的对对象的引用在其他方法中没有用。而且确实是这样。
    解决办法是在对对象的引用的初始化中,不能简单的用

    来初始化
    callBackObj = callback
    

    但有

    callbackObj = env->NewGlobalRef(callback)
    

    同样的问题:Objective C calling Java methods using JNI

    【讨论】:

    • 感谢您的评论。我不得不把这件事保持原样,去做另一项任务。但感谢您的回复。接缝是相关的。我会检查它(迟早)。顺便说一句,你编辑你的帖子了吗?通知不同!
    • 不客气,顺便说一句,很抱歉回复晚了。我编辑了我的帖子吗?嗯……可能是。这是我的第一个答案之一,我在回复格式方面遇到了一些问题。此外,fbfcn 纠正了我的语法(顺便感谢他)。
    【解决方案4】:

    创建一个 JNI init(JNIEnv * env, jclass c (or jobject o) ) 和

    save param #1 JNIEnv
    
    save param #2 jclass (if static) 
       or 
    save param #2 jobject (in non-static)
    
    lookup and save the jmethodID(s) for the Java method(s) you will be invoking.  
    

    还有一个 JNI 关闭(JNIEnv * env, jclass (or jobject)) 用于本机关闭/清理的好主意

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-04
      • 1970-01-01
      • 1970-01-01
      • 2019-04-09
      相关资源
      最近更新 更多