[英]SWIG (Java): How do I pass a struct with callback functions to C++ from an Android application?
我正在使用基于 WebRTC 的 C++ 代码库为 Android 开发实时通信应用程序(视频和音频)。 我使用 SWIG 生成一个 JNI 桥来从 Java 访问本机代码。 调用的行为是通过许多回调函数确定的,这些回调函数在应用程序层中定义并以结构体传递给库代码。 传递这些回调的函数如下所示:
void registerCallbacks(CALL_CALLBACKS* callbacks);
其中CALL_CALLBACKS
是一个包含许多回调函数的结构,如下所示:
struct CALL_CALLBACKS {
// CALL_STATE is an Enum
// Called whenever the state of the call changes
void (*statusCallback)(CALL_STATE);
// FRAME is a struct representing a video frame
// Called whenever the other participant sends a video frame, typically at 30 fps
void (*frameCallback)(const FRAME*);
// ...
}
问题是当我默认让 SWIG 做它的事情时,结果是不可用的。 它生成一个 Java 类型CALL_CALLBACKS
,其中包含每个回调的 setter 和 getter,它们也是 SWIG 生成的类型。 但是,这些类型是按照SWIGTYPE_p_f_ENUM_CALL_STATUS__void
命名的, SWIGTYPE_p_f_ENUM_CALL_STATUS__void
是 C 指针的包装器。
如何编写我的 SWIG 接口文件以便将回调(最好使用较少无意义的名称)传递给 C++ 库? 我认为可以以某种方式使用类型映射,但我无法理解如何做到这一点。
我不认为 SWIG 可以自己做到这一点。 这是我在普通 JNI 中的做法:
首先,创建一个可以在 Java 端实现的 Java 接口。
interface Callbacks {
void statusCallback(int status);
void frameCallback(Frame frame);
static native void registerCallbacks(Callbacks cb);
}
接下来,创建将 C++ 参数转发给实现该接口的jobject g_receiver
:
jobject g_receiver;
void my_statusCallback(CALL_STATE s) {
if (!g_receiver) {
// Print a warning?
return;
}
JNIEnv *env = getEnv();
env->PushLocalFrame(10);
jclass cls_Callbacks = env->GetObjectClass(g_receiver);
jmethodID mid_Callbacks_statusCallback = env->GetMethodID(cls_Callbacks, "statusCallback", "(I)V");
env->CallVoidMethod(g_receiver, mid_Callbacks_statusCallback, s);
env->PopLocalFrame(nullptr);
}
void my_frameCallback(const FRAME* frame) {
if (!g_receiver) {
// Print a warning?
return;
}
JNIEnv *env = getEnv();
env->PushLocalFrame(10);
// Create a Frame object from the C++ pointer.
// See Proxy classes at http://swig.org/Doc4.0/Java.html#Java_imclass
jclass cls_Frame = env->FindClass("Frame");
jmethodID ctr_Frame = env->GetMethodID(cls_Frame, "<init>", "(JZ)V");
jobject jFrame = env->NewObject(cls_Frame, ctr_Frame, (jlong) frame, (jboolean)false);
jmethodID mid_Frame_delete = env->GetMethodID(cls_Frame, "delete", "(LFrame;)V");
jclass cls_Callbacks = env->GetObjectClass(g_receiver);
jmethodID mid_Callbacks_frameCallback = env->GetMethodID(cls_Callbacks, "frameCallback", "(LFrame;)V");
env->CallVoidMethod(g_receiver, mid_Callbacks_frameCallback, jFrame);
env->CallVoidMethod(jFrame, mid_Frame_delete); // Disconnect the Java Frame object from the C++ FRAME object.
env->PopLocalFrame(nullptr);
}
CALL_CALLBACKS global_callbacks = { my_statusCallback, my_frameCallback };
最后,您可以按如下方式实现Callbacks#registerCallbacks
。 这有点棘手,因为您必须确保 g_receiver 是 nullptr 或有效的全局引用:
JNIEXPORT void Java_Callbacks_registerCallbacks(JNIEnv *env, jclass cls_Callbacks, jobject receiver) {
if (g_receiver) {
env->DeleteGlobalRef(g_receiver);
}
g_receiver = receiver ? env->NewGlobalRef(receiver) : nullptr;
registerCallbacks(global_callbacks);
}
我做了一些假设:
getEnv
函数应该使用 JNI 调用 API 将当前线程附加为守护线程。 您可以将指针存储在另一个全局变量中。FindClass
。 您可以通过在JNI_Onload
方法中创建对类的全局引用来解决此问题,或者通过向Callbacks
接口添加Class<Frame> getFrameClass()
方法来绕过它。 或者,您可以通过执行等效的g_receiver.getClass().getClassLoader().findClass("Frame")
来走很长的路。frameCallback
是否需要释放 FRAME 对象本身。 我的代码假定它没有并断开 Java 对象的连接,因此您不会在回调结束后意外使用它。registerCallbacks
,所以我认为它可以。 您还可以将registerCallbacks
作为JNI_Onload
一部分JNI_Onload
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.