您提供的代码段展示了如何在 JNI 中注册一个回调,以便 C++ 代码能够通过 Java 回调方法通知某些事件(如手势识别结果)。以下是对这段代码的逐行解析以及一些优化和注意事项:
代码解析
函数签名:
JNIEXPORT void JNICALL Java_com_ylkj_sdk_RknnWrapper_registerGestureResultCallback(JNIEnv *env, jclass, jobject callbackObj)
JNIEXPORT
和JNICALL
是用于标识 JNI 函数导出及调用约定。- 方法名称遵循 JNI 命名规则,表示它是从
com.ylkj.sdk.RknnWrapper
类调用的方法。 - 接受三个参数:JNI 环境指针、类引用(未使用)、Java 回调对象。
获取回调对象的类信息和方法 ID:
jclass callbackClass = env->GetObjectClass(callbackObj); gestureResultMethodId = env->GetMethodID(callbackClass, "GestureResultCallBackFunc", "(Ljava/util/ArrayList;)V"); gestureResultCallbackJobj = env->NewGlobalRef(callbackObj);
- 使用
GetObjectClass
获取回调对象的类。 - 使用
GetMethodID
获取要调用的方法 ID,这里假设这个方法接受一个ArrayList
作为参数并返回void
。 - 将回调对象转换为全局引用以防止其被垃圾回收。
- 使用
设置手势结果回调:
rknnEngine.SetGestureResultCallbackFunc([](const std::vector<obs_location_t> &locations){ ... });
- 在这里,您定义了一个 lambda 函数,该函数将在手势检测时被调用。
处理传入的数据:
JNIEnv* localEnv; jint getEnvResult = gvm->AttachCurrentThread(reinterpret_cast<JNIEnv**>(&localEnv), nullptr); if (getEnvResult == JNI_OK) { ... gvm->DetachCurrentThread(); }
- 您将当前线程附加到 JVM 以获取
JNIEnv
。这在多线程环境中非常重要,因为每个线程都有自己的本地环境。
- 您将当前线程附加到 JVM 以获取
构建 ArrayList 并填充数据:
jobject locationListObj = localEnv->NewObject(arrayListCls, arrayListCtor); for (int i = 0; i < locations.size(); ++i) { obs_location_t location = locations.at(i); jobject locationObj = localEnv->NewObject(locationCls, locationCtor, location.location, location.exist_person, location.raise_hand); localEnv->CallBooleanMethod(locationListObj, arrayListAddMethod, locationObj); localEnv->DeleteLocalRef(locationObj); }
- 创建一个新的
ArrayList
实例,然后遍历 C++ 的位置向量,将每个元素添加到 Java 的数组列表中。
- 创建一个新的
调用 Java 回调方法:
localEnv->CallVoidMethod(gestureResultCallbackJobj, gestureResultMethodId, locationListObj);
清理局部引用和分离线程: 在使用完本地引用后,通过
DeleteLocalRef()
清除,并在最后使用DetachCurrentThread()
分离该线程。
注意事项与优化建议
错误处理:在实际应用中,你可能希望检查各个 JNI 调用是否成功,例如检查
GetMethodID
, 创建全局引用等。这可以帮助你捕捉潜在问题并避免崩溃。性能考量:尽量减少对 JNI 的频繁调用,因为跨语言边界会带来额外开销。考虑批处理或合并操作来提高性能。
内存管理:确保在不再需要全局引用时使用
DeleteGlobalRef()
来释放它们,以避免内存泄漏。异常处理:JNI 调用后,可以检查是否抛出了异常,确保代码健壮性。例如,在调用方法之前检查:
if (localEnv->ExceptionCheck()) {
// 处理异常
}
以上是关于您的代码段的一些分析和改进建议。如果有其他具体问题或场景,请告诉我!
内容由零声教学AI助手提供,问题来源于学员提问