如果您有机会通过回调传递一些数据,则可以执行此操作,但是您需要编写一些JNI胶。我整理了一个完整的示例,说明如何将C样式的回调映射到Java接口。
您需要做的第一件事是确定在Java方面合适的接口。我假设在C中我们有如下回调:
typedef void (*callback_t)(int arg, void *userdata);
我决定用Java将其表示为:
public interface Callback { public void handle(int value);}(
void *userdata因为我们可以将状态存储在琐碎
Object实现的Java中,所以Java方面的损失不是真正的问题
Callback)。
然后,我编写了以下头文件(它实际上不应该只是头文件,但它使事情变得简单)来进行包装:
typedef void (*callback_t)(int arg, void *data);static void *data = NULL;static callback_t active = NULL;static void set(callback_t cb, void *userdata) { active = cb; data = userdata;}static void dispatch(int val) { active(val, data);}我能够使用以下接口成功包装此C:
%module test%{#include <assert.h>#include "test.h"// 1:struct callback_data { JNIEnv *env; jobject obj;};// 2:void java_callback(int arg, void *ptr) { struct callback_data *data = ptr; const jclass callbackInterfaceClass = (*data->env)->FindClass(data->env, "Callback"); assert(callbackInterfaceClass); const jmethodID meth = (*data->env)->GetMethodID(data->env, callbackInterfaceClass, "handle", "(I)V"); assert(meth); (*data->env)->CallVoidMethod(data->env, data->obj, meth, (jint)arg);}%}// 3:%typemap(jstype) callback_t cb "Callback";%typemap(jtype) callback_t cb "Callback";%typemap(jni) callback_t cb "jobject";%typemap(javain) callback_t cb "$javainput";// 4:%typemap(in,numinputs=1) (callback_t cb, void *userdata) { struct callback_data *data = malloc(sizeof *data); data->env = jenv; data->obj = JCALL1(NewGlobalRef, jenv, $input); JCALL1(DeleteLocalRef, jenv, $input); $1 = java_callback; $2 = data;}%include "test.h"该接口包含很多部分:
- 一个
struct
存储要对Java接口的调用所需的信息。 - 的实现
callback_t
。它接受struct
我们刚刚定义的用户数据,然后使用一些标准的JNI将调用调度到Java接口。 - 某些类型映射会导致
Callback
对象作为real直接传递给C实现jobject
。 - 一个类型图,它
void*
在Java端隐藏,并设置callback
数据并为实函数填充相应的参数,以使用我们刚刚编写的用于将调用分派回Java的函数。它采用对Java对象的全局引用,以防止随后对其进行垃圾回收。
我写了一个Java类来测试它:
public class run implements Callback { public void handle(int val) { System.out.println("Java callback - " + val); } public static void main(String argv[]) { run r = new run(); System.loadLibrary("test"); test.set(r); test.dispatch(666); }}如您所愿。
需要注意的几点:
- 如果您
set
多次调用,它将泄漏全局引用。您或者需要提供一种取消设置回调的方法,防止多次设置或使用弱引用。 - 如果您有多个线程,您将需要
JNIEnv
比这里的人更加聪明。 - 如果要混合使用Java和C的回调实现,则需要在此解决方案上进行大量扩展。您可以将C函数公开用作回调函数,
%constant
但是这些类型映射将阻止您的包装函数接受此类输入。可能您想提供重载来解决此问题。
我相信C#的解决方案会有些相似,不同的Typemap名称和用C编写的回调函数的不同实现。



