java - JNI 和构造函数

标签 java c++ constructor java-native-interface

我有一个需要在项目中使用的编译库。简而言之,它是一个用于与特定硬件交互的库。我拥有的是 .a 和 .dll 库文件,分别用于 linux 和 windows,以及一堆 C++ .h header ,其中描述了所有公共(public)函数和类。

问题是项目需要使用 Java,所以我需要为这个库编写一个 JNI 包装器,老实说,我从来没有这样做过。不过没关系,我正在努力学习这件事。

我已经在线阅读了一堆文档,并且弄清楚了传递变量、从 native 代码创建 java 对象等。

我想不通的是,如何使用 JNI 处理 native 构造函数?我不知道这些构造函数的源代码是什么,我只有这样的标题:

namespace RFDevice {

class RFDEVICE_API RFEthernetDetector
{
public:
    //-----------------------------------------------------------------------------
    //  FUNCTION  RFEthernetDetector::RFEthernetDetector
    /// \brief    Default constructor of RFEthernetDetector object.
    ///           
    /// \return   void : N/A
    //-----------------------------------------------------------------------------
    RFEthernetDetector();
    RFEthernetDetector(const WORD wCustomPortNumber);

所以基本上如果我要用 C++ 编写我的程序(我不会),我会做类似的事情

RFEthernetDetector ethernetDetector = new RFEthernerDetector(somePort);

然后使用该对象。但是...我如何使用 JNI 在 Java 中执行此操作? 我不明白我应该如何为构造函数创建一个 native 方法,它会从我的 .a 库调用构造函数,然后以某种方式处理该特定对象?我知道如何从 native 代码创建 Java 对象 - 但问题是我没有关于 RFEthernetDetector 类的内部结构的任何信息 - 只有其中的一些公共(public)字段和公共(public)方法。

而且我似乎无法在网上找到合适的文章来帮助我。我该怎么做?

更新:进一步说明。

我像这样创建一个 .java 包装器类:

public class RFEthernetDetector
{
    public RFEthernetDetector(int portNumber)
    {
        Init(portNumber);
    }

    public native void Init(int portNumber);            // Void? Or what?
}

然后我用 -h 参数编译它来生成 JNI .h 文件:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class RFEthernetDetector */

#ifndef _Included_RFEthernetDetector
#define _Included_RFEthernetDetector
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     RFEthernetDetector
 * Method:    Init
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_RFEthernetDetector_Init
  (JNIEnv *, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif

然后我创建一个实现,它将调用我的 .a 库中的函数:

#include "RFEthernetDetector.h"     // auto-generated JNI header
#include "RFEthernetDetector_native.h"  // h file that comes with the library, 
                    //contains definition of RFEthernetDetector class
/*
 * Class:     RFEthernetDetector
 * Method:    Init
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_RFEthernetDetector_Init(JNIEnv *env, jobject thisObj, jint value)
{
    RFEthernetDetector *rfeDetector = new RFEthernetDetector(value);    // constructor from the library
    // now how do I access this new object from Java?
    // if I need to later call rfDetector->doSomething() on that exact class instance?
}

最佳答案

您需要构建一个 RFEthernetDetector Java 类,它通过指针拥有 C++ 端的一个RFEthernetDetectorThis is no fun ,但语言间胶水永远不会。

// In this design, the C++ object needs to be explicitly destroyed by calling
// close() on the Java side.
// I think that Eclipse, at least, is configured by default to complain
// if an AutoCloseable is never close()d.
public class RFEthernetDetector implements AutoCloseable {
   private final long cxxThis; // using the "store pointers as longs" convention
   private boolean closed = false;
   public RFEthernetDetector(int port) {
       cxxThis = cxxConstruct(port);
   };
   @Override
   public void close() {
       if(!closed) {
           cxxDestroy(cxxThis);
           closed = true;
       }
   }
   private static native long cxxConstruct(int port);
   private static native void cxxDestroy(long cxxThis);

   // Works fine as a safety net, I suppose...
   @Override
   @Deprecated
   protected void finalize() {
       close();
   }
}

在 C++ 方面:

#include "RFEthernetDetector.h"

JNIEXPORT jlong JNICALL Java_RFEthernetDetector_cxxConstruct(JNIEnv *, jclass, jint port) {
    return reinterpret_cast<jlong>(new RFEthernetDetector(port));
}

JNIEXPORT void JNICALL Java_RFEthernetDetector_cxxDestroy(JNIEnv *, jclass, jlong thiz) {
    delete reinterpret_cast<RFEthernetDetector*>(thiz);
    // calling other methods is similar:
    // pass the cxxThis to C++, cast it, and do something through it
}

如果所有这些reinterpret_casting 让您感到不舒服,您可以选择保留一个map:

#include <map>

std::map<jlong, RFEthernetDetector> references;

JNIEXPORT jlong JNICALL Java_RFEthernetDetector_cxxConstruct(JNIEnv *, jclass, jint port) {
    jlong next = 0;
    auto it = references.begin();
    for(; it != references.end() && it->first == next; it++) next++;
    references.emplace_hint(it, next, port);
    return next;
}

JNIEXPORT void JNICALL Java_RFEthernetDetector_cxxDestroy(JNIEnv *, jclass, jlong thiz) {
    references.erase(thiz);
}

关于java - JNI 和构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58178431/

相关文章:

java - 在 hibernate POJO 类中实现 java.io.Serializable

java - Gurobi 模型最优但违反约束

java - Java 中的 XSD 设计问题

c++ - 带 CDB 的 QtCreator 3 在调试器中显示错误值

java - 对于带有 JAXB 注释的类,是否有类似 PostConstruct 的东西?

java - 这里调用父类(super class)构造函数的是什么?

java - Sqlite 列到字符串数组

c++ - 我不明白我在此编程代码中遇到的错误

c++ - 使用 convertTo 从 CV_16SC1 转换为 CV_32FC1

Java构造函数和初始化类变量