JNI和构造函数

 2023-01-20    295  

问题描述

我有一个我需要在项目中使用的编译库.简而言之,它是一个与特定硬件进行交互的库.我所拥有的是.a和.dll库文件,分别用于Linux和Windows,以及一堆C ++ .H标题,其中所有公共功能和类别都描述了.

问题是该项目需要在Java中,因此我需要为此库编写JNI包装,老实说,我从来没有这样做.但这没关系,我很想学习东西.

JNI和构造函数

我已经在线阅读了一堆文档,我发现了通过变量,从本机代码中创建Java对象等.

我不知道的是如何使用JNI与本机构造函数一起工作?我不知道这些构造函数的源代码是什么,我只有这样的标题:

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中这样做?
我不明白我应该如何为构造函数创建本机方法,该方法会从我的.a库中调用构造函数,然后有某种方式与该特定对象合作?我知道如何从本机代码中创建Java对象 – 但事实是,我没有关于Rfethernetdetector类内部结构的任何信息 – 只有其中一些公共字段和公共方法.

我似乎找不到网上正确的文章来帮助我.我该怎么做?

更新:进一步澄清.

我创建了一个.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

i然后创建一个实现,该实现将从我的.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类,该类别通过指针, a RFEthernetDetector在C ++方面. 这不是一个有趣的

// 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_cast使您感到不舒服,则可以选择在周围保持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中构建native类,然后运行javah程序,该程序将构建Java期望的存根.然后,您需要将Java存根映射到C ++代码,以与您的Java程序一起编译和分发该绑定库.

/home/ehchua/programming/java/javanativeinterface.html

其他推荐答案

因此,我现在最终做的是基本上将地址存储在我的.java类中为”长”变量,然后让init()方法返回C ++实例的地址为Jlong.

然后,当我需要在类的那个实例上调用某些东西时 – 我将地址作为附加参数传递,并在这样的C代码中进行转换(在测试类/custom .SO上进行了测试):

//constructor wrapper
JNIEXPORT jlong JNICALL Java_Test_Greetings(JNIEnv *env, jobject thisObj, jint value)
{
    Greetings *greetings = new Greetings(value);
    return (jlong)greetings;
}

JNIEXPORT void JNICALL Java_Test_HelloWorld(JNIEnv *env, jobject thisObj, jlong reference)
{
    Greetings *greetings;
    greetings = (Greetings*)reference;
    greetings->helloValue();
}

我不知道那是正确的方法,但是它可以奏效…如果有人告诉我我有多错.

以上所述是小编给大家介绍的JNI和构造函数,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/26152.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。