JNI与C ++对象实例

时间:2009-04-28 08:40:48

标签: c++ java-native-interface instance

我刚开始接受新工作。在这里,我们是使用JNI(用于桥接C ++ / Java)的新手。我是JNI的新手所以请原谅我的noobness:)

在我们的(win32)Java应用程序中,我们正在加载C ++ DLL。在Java方面,我们有几个“SomeJClass”实例,每个实例都需要访问DLL端的“SomeCClass”对应实例。 DLL公开入口点,例如GlobalDoSomethingInC()。在这里,我必须调用Doer :: DoSomethingInC()的实例方法。所以我需要一种平滑的方法来映射各自的指针。 当DLL线程发现需要通知相应Java实例的有趣内容时,我还需要执行相同的映射。

我可以想到几种解决方案,但我不太喜欢它们。我的问题是,还有比这更好的方法吗?

1 Java调用C:GetNewInstance()。这将返回一个int,它实际上是指向新C实例的指针。 Java将其存储在m_myCInstance中。然后Java调用GlobalDoSomethingInC(),和 1a

// DLL global
void GlobalDoSomethingInC()
{
    // retrive this pointer
    //calling back to Java:  
    jobj tmpJ = NewGlobalRef( env, obj );
    Doer* myDoer = <reinterpret_cast>( Doer )tmpJ->GetMyCInstance();
    myDoer->DoSomething();
    DeleteGlobalRef( env, tmpJ );
    // Arrrrgh
}

1b或:

    // for **every call** that Java adds a parameter, 
    //which is the stored int:m_myCInstance, and
    Doer* myDoer = <reinterpret_cast>( Doer )instanceParam->DoSomethingInC();
    // Can we do better that this?

2对于从C到Java的调用,事情看起来可能更好

In the constructor C calls back into Java and stores
the Java instance reference 
    in a member variable. m_myJInstance.
    In all subsequent calls m_myJInstance can be used to call back Java.
    In the destructor we need to call DeleteGlobalRef( env, m_myJInstance );

我想也不错。但是存储jobject引用真的很安全。 我的意思是:当GC移动物体时会发生什么?

3我们目前的解决方案确实“有效”。但它属于http://www.codinghorror.com/blog/:)

感谢名单

2 个答案:

答案 0 :(得分:2)

通常这取决于您的环境。我只使用了KNI,它比JNI更原始。我认为相当丑陋是不可避免的,因为你在两个系统中混合了内存跟踪,其中只有一个系统有GC。

一般来说,我发现最好将所有调用C代码的函数包含在处理令人讨厌的转换的函数中,我认为这是不可避免的。 (顺便说一句,我将在这里使用C表示非Java代码)

在C方面,Java对象的移动肯定是一个潜在的问题。它将取决于您的平台,但我希望只要您在lib中,就可以预期不会发生Java GC,因此您的对象是稳定的。你需要确定这一点。另一方面,如果情况并非如此,那你几乎搞砸了。假设是这种情况,你想做同样的事情,将解除引用/转换隔离到暴露给JNI的函数,这样你就可以愉快地使用所有被调用函数中的普通C对象。

如果你可以让对象超出范围,那么它可能会变得非常难看,因为那时任何一方都可能持有对象的引用。这里我们在Java端使用终结器,在C端使用析构器。它不漂亮,但我觉得有些不可避免。

所以,简短的回答,它会有点丑陋,孤立两种语言之间界面的丑陋,所以对于大部分工作,无论使用哪种语言,你都不必担心这些事情。 / p>

对于存在于此界面上的对象,也值得拥有一个基类,因为在这里你也可以隔离一些丑陋。

答案 1 :(得分:0)

jobject是对象的不透明句柄。可能在运行时实现方面有所不同(请参阅Android 2.x与4.x),但只要相信它是一个不透明的对象。

目前的解决方案可能是正确的。如果必须在本机代码中存储jobject,则必须将其转换为全局引用 - 如果调用NewGlobalRef,则对象的引用数量会增加,并且在调用DeleteGlobalRef之前不会释放(并且GC已注意到否则无法访问)