当前位置: 首页 > 知识库问答 >
问题:

SWIG(Java):如何从Android应用程序将带有回调函数的结构传递给C?

蔺弘
2023-03-14

我正在使用基于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的行命名的,只不过是C指针的包装器。

如何编写SWIG接口文件,以便将回调(最好使用不那么荒谬的名称)传递到C库?我认为可以以某种方式使用打字图,但我就是不知道该怎么做。


共有1个答案

龙俊英
2023-03-14

我不认为SWIG自己能做到这一点。下面是我在普通JNI中的实现方式:

首先,创建一个可以在Java端实现的Java接口。

java prettyprint-override">interface Callbacks {
  void statusCallback(int status);
  void frameCallback(Frame frame);
  static native void registerCallbacks(Callbacks cb);
}

接下来,创建将C参数转发到实现接口的jobjectg_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 };

最后,您可以实现Callback#ynsterCallback,如下所示。这有点棘手,因为您必须确保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);
}

我做了一些假设:

  • 生成代码时没有使用任何包。这将影响JNI方法名和签名中对类的任何引用
 类似资料:
  • 问题内容: 我有一个用Java实现的javascript接口,该接口由Web视图中加载的javascript代码调用。 JS Inside webview: Java: 通过webview加载的字符串最终是: 我有一些想法: 一个。在JS中以字符串形式构建回调。 b。在将回调传递给Android.myFunction()之前,先调用回调的toString(); 我的问题是,最好的方法是什么?我希望

  • 问题内容: 我在C 中有一个方法,该方法将双精度数组作为参数。我从Java调用此方法,需要传递一个双精度数组。C 例程读取和修改数组的值,而我需要Java中的那些更新后的值。我该怎么做呢? 例如,使用C ++例程: 和Java代码: 我猜不能像上面的调用那样对myMethod进行调用…还是可以吗?而在Swig中进行这项工作所需的是什么。如果我无法进行上述调用,如何将我的值获取到C ++代码? 问题

  • 二.背景 我正在使用C代码库为Android开发一个实时通信应用程序。我使用SWIG生成一个JNI桥,以从Java访问本机代码。为了跟踪正在进行的通话,将用作句柄(指向包含正在进行的通话信息的地址)。以下函数头是如何使用它的示例:

  • 问题内容: 我试图将回调函数从控制器传递给指令。 这是回调函数代码: 指令用法: 指令代码: 模板中的回调用法: 但是,这给了我以下错误: 我看过很多类似的问题,但不明白我在哪里错了。 问题答案: 从指令中调用表达式方法时,您需要以格式传递指令中的参数,还应更正指令属性值以像 指令用法: 指令模板

  • 问题内容: 我正在尝试将一些参数传递给用作回调的函数,该怎么做? 问题答案: 如果您想要更一般的东西,可以使用arguments变量,如下所示: 但是否则,您的示例可以正常工作(可以在测试器中使用arguments [0]代替回调)

  • 问题内容: 我正在这样运行我的JavaFX应用程序: 类扩展。在特殊的FX线程中启动JavaFX窗口,但是在我的main方法中,我什至没有类的实例。 如何将非字符串参数(在我的情况下为 控制器 )传递给实例?它是有缺陷的设计吗? 问题答案: 通常,除了传递给您的主程序的程序参数外,无需将参数传递给主应用程序。想要这样做的唯一原因是创建一个可重用的对象。但这并不需要是可重用的,因为这是组装您的应用程