使用JNI从c ++调用java方法

时间:2012-03-12 14:26:48

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

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

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

DWORD callbackFunction(void *params);

所以我在JNI dll中实现了这样的函数,并希望在调用此函数时调用Java wapper中的函数。

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


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

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是一个接口,它的方法是在另一个calss中实现的,即“callBackClass”

4 个答案:

答案 0 :(得分:5)

您需要引用当前的JVM:

JavaVM *jvm;

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

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

在观察USB attachemnt / detachment时,您可以从这个JavaVM获取JNIEnv,如下所示:

JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);

//your code here

jvm->DetachCurrentThread();

这是为了使每个USB设备更改创建一个新线程。如果你只使用一个线程进行chceking,你只需要连接一次(在initilizer中,也许?),只要你的本机线程保持连接到JVM,你就会有JNIEnv有效。

答案 1 :(得分:2)

您可能需要做的是在C中创建一个队列并等待它或使用Java线程轮询它。这总是可以使用当前的JNIEnv。


看来你不能......

....从最后一次设置JNI的JNI调用中保存JNIENV并重新使用它。

您的回调似乎会返回您在设置回叫时可能已经通过的参数。您可以将其中一个作为JNIENV。

答案 2 :(得分:1)

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

callBackObj = callback

但是

callbackObj = env->NewGlobalRef(callback)

此处的问题相同:Objective C calling Java methods using JNI

答案 3 :(得分:0)

创建一个JNI init(JNIEnv * env,jclass c(或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(或jobject))本机关闭/清理的好主意