2

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

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

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

1 Java 调用 C:GetNewInstance()。这将返回一个实际上是指向新 C 实例的指针的 int。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 );

我想还不错。但是存储对象引用确实很安全。 我的意思是:当 GC 移动对象时会发生什么?

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

谢谢

4

2 回答 2

2

通常,这在一定程度上取决于您的环境。我只用过KNI,比JNI还要原始。我认为有些丑陋是不可避免的,因为您在两个系统中混合内存跟踪,其中只有一个系统具有 GC。

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

在 C 方面,Java 对象的移动绝对是​​一个潜在的问题。这将取决于您的平台,但我希望只要您在 lib 中,就不会发生 Java GC,因此您的对象是稳定的。您需要确定这一点。另一方面,如果不是这样,那你就完蛋了。假设是这种情况,您希望对暴露给 JNI 的函数进行隔离解引用/强制转换,以便您可以愉快地在所有被调用函数中使用普通 C 对象。

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

所以,简短的回答,它会有点丑陋,隔离两种语言之间接口周围的丑陋,这样对于大部分工作,无论是哪种语言,你都不必担心这些事情。

为存在于该接口上的对象创建一个基类也是值得的,因为在这里您还可以隔离一些丑陋的东西。

于 2009-04-28T09:11:28.893 回答
0

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

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

于 2013-04-30T23:44:39.430 回答