从C+;返回函数指针+;使用JNI对Java对象的行为

从C+;返回函数指针+;使用JNI对Java对象的行为,java,c++,java-native-interface,function-pointers,swig,Java,C++,Java Native Interface,Function Pointers,Swig,这已经超过我的能力了,我们开始吧 我在C++中设置了一个函数指针,它的java对象看起来像java的边: Info_t info_t = new Info_t(); info_t.setCallbackTest(new CallbackTest() { @Override public void onCallback(int num) { System.out.println("callback: " + num);

这已经超过我的能力了,我们开始吧

我在C++中设置了一个函数指针,它的java对象看起来像java的边:

    Info_t info_t = new Info_t();
    info_t.setCallbackTest(new CallbackTest() {
        @Override
        public void onCallback(int num) {
            System.out.println("callback: " + num);
        }
    });
其中,
CallbackTest()
是仅具有一种方法的接口:

public interface CallbackTest {
    void onCallback(int num);
}
<>和 StCalbBestTestTo()/Cuth>引用java原生方法,它在C++方面反映如下:

JNIEnv *gbl_env;
jmethodID gbl_method;
jobject gbl_callback;

void WrapperFunction(int a) {
    gbl_env->ExceptionClear();
    gbl_env->CallVoidMethod(gbl_callback, gbl_method, a);
    if(gbl_env->ExceptionOccurred()) {
        gbl_env->ExceptionClear();
    }
    return;
}

JNIEXPORT void JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2) {
    Info *arg1 = (Info *) 0;

    (void) jenv;
    (void) jcls;
    (void) jarg1_;
    arg1 = *(Info **)&jarg1;
    {
        jclass clazz;
        jmethodID mid;

        clazz = jenv->GetObjectClass(jarg2);
        mid = jenv->GetMethodID(clazz, "onCallback", "(I)V");
        if(mid == 0) {
            std::cout << "could not get method id" << std::endl;
            return;
        }
        gbl_env = jenv;
        gbl_method = mid;
        gbl_callback = jarg2;
    }
    // here I am setting function pointer to the helper method which invokes the java code
    if(arg1) (arg1)->completionCB = WrapperFunction;
}
因此,稍后我想将
callbackFunction completionCB
作为一个对象返回Java,并可能对其进行各种处理

我的问题是,如何将函数指针行为从C++返回到java对象?基本上,我也想这样做,但顺序相反。我现在可以将Java接口映射到函数指针上了,我想将函数指针行为映射到Java对象上

编辑:到目前为止,我已经想出了这样的getter,但我不知道如何继续

JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {
    jobject jresult = 0;
    Info *arg1 = (Info *) 0;
    callbackFunction result;

    (void)jenv;
    (void)jcls;
    (void)jarg1_;
    arg1 = *(Info **)&jarg1;
    result = ((arg1)->completionCB);

    {
        jclass clazz = jenv->FindClass("com/CallbackTest");
        ???
    }
    ???
    return ;
}

不能缓存JNIEnv值

根据JNI规范(加粗矿):

连接到虚拟机

JNI接口指针(JNIEnv)仅在当前 线程。如果另一个线程需要访问Java VM,它必须 首先调用AttachCurrentThread()将自身连接到VM,然后 获取JNI接口指针。一旦连接到VM,本机 线程的工作原理与在本机中运行的普通Java线程类似 方法。在调用之前,本机线程将一直连接到VM
DetachCurrentThread()

对于方法id和回调对象,需要创建全局引用:

    gbl_method = jenv->NewGlobalReference( mid );
    gbl_callback = jenv->NewGlobalReverend( jarg2 );
不过,这确实产生了一个严重的问题,即
gbl_callback
中的对象引用将导致Java对象永远不会被垃圾收集

<> >返回一个C++回调函数,首先需要将函数指针转换为有效的java类型。严格遵守C标准(JNI是C而不是C++),所以在C++中混合时,传递对象会有点模糊,意味着不能将函数指针当作一系列字节以外的任何东西来对待,这意味着需要将函数指针转换为java字节数组并将其返回给java。然后,当您想在本机代码中使用Java字节数组时,将其转换回函数指针。这是很多代码,在JNI中,编写的代码越多,就越有可能破坏某些东西——JNI是脆弱的

但在Windows和POSIX上,函数指针都可以放入
jlong
,而
jlong
可以准确地表示函数指针的值,所以这是一种可行的方法(在POSIX下,它甚至可能不是一个黑客,但我不打算尝试寻找允许该转换的实际POSIX函数指针规范)

最简单的方法是在Java对象中创建一个
long
字段,并将函数指针转换为
jlong

JNIEXPORT jlong JNICALL Java_apiJNI_Info_1t_1callbackTest_1set(
    JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jobject jarg2)
{
    ...

    return( ( jlong ) WrapperFunction );
}
这将被称为类似这样的东西:

// Info_t has a long field called callback
Info_t info_t = new Info_t();
info_t.callback = info_t.setCallbackTest(new CallbackTest() {
    @Override
    public void onCallback(int num) {
        System.out.println("callback: " + num);
    }
});
根据我的经验,您进行的JNI调用越少越好。因此,如果您可以通过参数传递值或从函数调用返回值,这比尝试通过JNI调用获取/设置对象字段值更简单、更安全、更可靠

但是您需要将回调值作为另一个参数传递:

// function pointer, returns void, takes int arg
typedef void (*funcPtr)( int );

JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(
JNIEnv *jenv, jclass jcls, jlong callback, jlong jarg1, jobject jarg1_)
{
    funcPtr f = ( funcPtr ) callback;

    f( 4 );

    ...
}

你是否正在对我定义的全局代码<代码> JEnv*/Cube >代码进行了修改,并编写了代码> GBLYEnV= jEnv;<代码>?如果你这样做,那么,如何将java接口映射到C++中的函数指针?@李利克,答案中的引文已经概述了需要做的事情。请看更多的细节。谢谢米迦勒。这指出了我的COD的缺陷。e但仍在寻找答案我如何将函数指针映射到Java对象。@Lisek指出了我代码的缺陷,但仍在寻找答案我如何将函数指针映射到Java对象。无法调用非JNI类型的函数,如
void WrapperFunction(int a)
直接来自Java代码。但是,您可以将函数指针保存为
jlong
值。有关详细信息,请参阅编辑。
// function pointer, returns void, takes int arg
typedef void (*funcPtr)( int );

JNIEXPORT jobject JNICALL Java_apiJNI_Info_1t_1callbackTest_1get(
JNIEnv *jenv, jclass jcls, jlong callback, jlong jarg1, jobject jarg1_)
{
    funcPtr f = ( funcPtr ) callback;

    f( 4 );

    ...
}