我正在尝试在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”
答案 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)
答案 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))本机关闭/清理的好主意