【问题标题】:Call C++ functions from Java从 Java 调用 C++ 函数
【发布时间】:2017-04-19 14:15:31
【问题描述】:

这是我在 StackOverflow 上的第一篇文章。我正在做一个项目,我需要你在 JNI 中的帮助。我被困在那里......我最近一直在阅读和尝试它,但仍然不知道如何让它工作。

我在 C++ 中创建了一个静态库(伪代码如下所示):

// file: X.h    
class X {
    public:
        X() {};
        ~X() {};

        void fooX() { // do stuff };
        void barX() { // do more stuff };
}

// file: X.cpp
#include "X.h";

// file: Y.h 
#include "X.h"
class Y {
    public:
        Y() {x = new X()};
        ~Y() {};

        fooY() { // do stuff };
        barY() { // do more stuff };

    private:
        X x;    // object of class X
}

// file: Y.cpp
#include "Y.h"

// file: Z.h 
#include "Y.h"
class Z {
    public:
        Z(uint8_t, std::string, std::vector<uint8_t>);
        ~Z() {};

        fooZ() { // do stuff };
        barZ() { // do more stuff };

    private:
        Y y;    // object of class Y
}

// file: Z.cpp
#include "Z.h"
Z::Z(uint8_t a, std::string b, std::vector<uint8_t> c)
{
     /// do stuff and create an object of Y
     Y y = new Y();
}

// file api.h
#include "Z.h"
void accessZ(uint8_t, std::string, std::vector<uint8_t>);

// file api.cpp
#include "api.h"
void accessZ(uint8_t a_uint, 
     std::string b_string, 
     std::vector<uint8_t> c_vector)
{
     // create object of Z
     Z z = new Z(a_uint, b_string, c_vector);
     z->fooZ();

     delete z;
     z = NULL; 
}

此外,我已将上述所有代码编译为 C++ 中的静态库 (libXYZ.a)(Windows 7 上使用 MinGW 的 Eclipse C++ CDT)。

现在,我想要的是能够从 Java 应用程序调用 API accessZ() C++ 函数! 换句话说,我已经用 C++ 实现了核心功能,我想用 Java 实现 GUI。因此,我需要从 Java GUI 访问 C++ 函数...

我该怎么做?我需要直接的答案,而不是模糊的答案...

非常感谢您的帮助。

非常感谢您的支持。

已编辑

我的项目如下:

                                MyLibrary
                                    |
Makefile        header/     source/        bin/       object/     lib/
                X.h         X.cpp                     X.o       libXYZ.a
                Y.h         Y.cpp                     Y.o
                Z.h         Z.cpp                     Z.o
                api.h       api.cpp                   api.o

我所需要的只是能够使用 accessZ() 函数将一些参数从 Java 传递到 libXYZ

新编辑:

public final class NativeClass {
  {
    System.loadLibrary("myLibrary");
  }

  public native void accessZ(char a_uint, String b_string, char[] c_vector);

  public static final NativeClass getInstance() {
    return INSTANCE;
  }

  private static final NativeClass INSTANCE = new NativeClass();
};

【问题讨论】:

  • 您查看过现有的任何 jni 教程吗?你为什么希望我们为你明确地写下东西……以前写过无数次?
  • @GhostCat :我当然检查过 JNI 教程。但是,它们都针对用 C++ 编写的特定函数,而不是库中的函数。
  • 在这些教程中......那里使用的 c++ 函数......没有存储在某个库或 dll 中——它们只是生活在真空中?!
  • 非常感谢您的 cmets 和时间...我对 JNI 完全陌生。因此,如果您知道/有一些有用的链接/教程(不像 HelloWorld JNI 示例)来解决我的问题,请将其放在那里;可能,我错过了一些搜索结果...否则,我没有时间进行无用的讨论...
  • 您必须向我们展示您迄今为止尝试过的内容,即调用 C++ 函数的 Java 代码。作为一个建议,最好从一个非常简单的函数开始你的实验,比如int getInt() { return 123; }。否则,不清楚您期望我们提供什么样的答案,不会复制大量现有的免费在线教程或关于 JNI 的教科书章节。请记住,Stack Overflow 是关于特定问题的。你也不应该听起来那么苛刻。我的意思是,“我需要直接的答案”,拜托……这不像是你付钱给我们。

标签: java c++ eclipse c++11 java-native-interface


【解决方案1】:

您不能直接调用这样的函数,您将从 JNI 调用的函数必须具有特定的名称和特定的形式,并且必须由某个 Java 类中的成员函数表示。 (另外,Java 没有无符号整数,因此您的 uint8_t 可能必须是 char 或其他一些原始类型,同样您需要考虑在 Java 中代表 std::vector 的内容。)但它是完全可以

  1. 使 Java 函数可公开访问(不幸的是,它不能是静态的,类实例很重要),

  2. 让本机实现除了提取参数并调用accessZ()之外什么都不做。

通常,您的代码将类似于

public final class NativeClass {

  {
    System.loadLibrary("myLibrary");
  }

  public native void accessZ(char a_uint, String b_string, char[] c_vector);

  public static final NativeClass getInstance {
    return INSTANCE;
  }

  private static final NativeClass INSTANCE = new NativeClass();

};

那么您的myLibrary.cpp 将必须包含&lt;jni.h&gt;"api.h" 并包含类似以下的函数

JNIEXPORT void JNICALL Java_NativeClass_accessZ(JNIEnv *env, jobject obj, jchar ja_uint, jstring jb_string, jcharArray jc_vector) {

  uint8_t a_uint = (uint8_t)ja_uint;

  const char *cfn = env->GetStringUTFChars(jb_string, 0);
  std::string b_string{cfn};
  env->ReleaseStringUTFChars(jb_string, cfn);

  size_t sz = env->GetArrayLength(jc_vector);
  std::vector<uint8_t> c_vector(sz);
  jchar *c_array = env->GetCharArrayElements(jc_vector, NULL);
  for(size_t i = 0; i < sz; i++)
    c_vector[i] = c_array[i];
  env->ReleaseCharArrayElements(jc_vector, c_array, 0);

  accessZ(a_uint, b_string, c_vector);

}

包裹在extern "C" 块中。您的 JNI 教程将告诉您如何使用它制作动态库并帮助 Java 找到它。只需在创建.dll 时将项目的其他部分 (libXYZ.a) 与它静态链接(即,在源中列出存档)。然后,您可以从 Java 中调用,例如:

NativeClass.INSTANCE.accessZ((char)10, "abc", new char[]{3, 5, 7});

这个函数将成为你的库的访问点,但是它可以做任何它想做的事情,它可以创建对象,调用它们的函数,它甚至可以保持一个瞬态,因为您的库将在调用之间继续运行。 (不过,最好对传递给函数的状态对象进行操作。)库中可能有任意许多这样的函数暴露给 Java。

这是我的命令行(适用于 Linux):

g++ -isystem /usr/java/jdk1.8.0_101/include/ -isystem /usr/java/jdk1.8.0_101/include/linux myLibrary.cpp -c -fPIC
(creates myLibrary.o)

g++ myLibrary.o libXYZ.a -shared -o libmyLibrary.so
(creates libmyLibrary.so)

【讨论】:

  • 嗨@The Vee,非常感谢您的回复。我将在今天晚些时候尝试(如果我有时间)并回复您。事实上,与此同时,我只需要使用 accessZ() 函数将参数从 Java 传递到 C++,该函数不返回任何内容(即 void)。稍后,当我完成这一步(Java -> C++ 通信)时,我希望 accessZ() 返回一个布尔值;但现在我只关注问题的一个部分。干杯。
  • @M_Square 我的代码包含一些错误。我现在对其进行了测试并修复了它们,所以请尝试使用新版本。
  • 非常感谢您的努力。我昨天(多次使用不同的设置)尝试使用 javah 进行编译,但是您上面提供的类没有编译...当我运行 javah -jni NativeClass.java 时出现以下错误:
  • Exception in thread "main" java.io.IOException: can't find class file NativeClass/java.class in java.net.URLClassLoader{urls=[file:/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre/lib/rt.jar], parent=gnu.gcj.runtime.SystemClassLoader{urls=[file:./], parent=gnu.gcj.runtime.ExtensionClassLoader{urls=[], parent=null}}} at gnu.classpath.tools.javah.Main.getClass(libgcj-tools.so.16) at gnu.classpath.tools.javah.Main.parseClasses(libgcj-tools.so.16) at gnu.classpath.tools.javah.Main.run(libgcj-tools.so.16) at gnu.classpath.tools.javah.Main.main(libgcj-tools.so.16) 有什么想法吗?BR
  • @M_Square 这是一个奇怪的名字,NativeClass/java.class。这是一个“找不到文件”错误,与文件 in 中的内容无关。编译其他类没有问题吗?你有NativeClass 遵循相同的文件命名条件吗?如果它是包x.y.z 中的公共类,则需要在名为x/y/z/NativeClass.java 的文件中找到它。 (此外,C 方法的名称是 Java_x_y_z_NativeClass_accessZ。)在我的示例中,它不在任何包中。
猜你喜欢
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多