使用JNI从C ++库加载特定类的方法

时间:2011-09-22 07:04:42

标签: c++ java-native-interface

我有一个C ++库xyz。它有很多类,例如xyzAxyzB等。我想使用xyz库中的类getAge()中的方法xyzA

xyz.so文件已存在。

我遵循的步骤:

  1. 创建了一个Java类xyz.java

    class xyz {
    
        public native int getAge();
    
        public static void main(String[] args) {
            new xyz().getAge();
        }
        static {
            System.loadLibrary("xyz");
        }
    }
    
  2. 为Java类创建了标题。

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class xyz */
    
    #ifndef _Included_xyz
    #define _Included_xyz
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     xyz
     * Method:    getAge
     * Signature: ()I
     */
    JNIEXPORT jint JNICALL Java_xyz_getAge
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
  3. cpp包装器类看起来像:

    #include <stdio.h>
    #include "xyz.h"
    #include <jni.h>
    
    JNIEXPORT jint JNICALL Java_xyz_getAge(JNIEnv *, jobject)
    {
        // some code
    }
    
  4. 我按如下方式成功编译了这个类:

    gcc -fPIC -shared -l stdc++ -I/grid/0/gs/java/jdk64/current/include -I/grid/0/gs/java/jdk64/current/include/linux xyz.cpp 
    
  5. 然后运行Java prog:

    java -Djava.library.path=/grid/0/tmp/direct/lib xyz
    

    我收到以下错误:

    Exception in thread "main" java.lang.UnsatisfiedLinkError: xyz.getAge()I
            at xyz.getAge(Native Method)
            at xyz.main(xyz.java:6)
    
  6. 找不到特定于类getAge()的方法xyzA。如何访问该方法?另外,库是通过我的包装器类链接的吗?

    任何指针都会受到赞赏。

    感谢。

3 个答案:

答案 0 :(得分:2)

如果您在Unix上运行,则共享库必须命名为libxyz.so,而不是xyz.so

答案 1 :(得分:0)

C ++库中的导出函数名称是 mangled :普通函数名称使用类和命名空间名称,参数和返回类型进行修饰。例如,以下是boost库中的一种方法:

        ?estimate_max_state_count@?$perl_matcher@PB_WV?$alloca
tor@U?$sub_match@PB_W@boost@@@std@@U?$regex_traits@_WV?$w32_regex_traits@_W@boos
t@@@boost@@@re_detail@boost@@AAEXPAUrandom_access_iterator_tag@std@@@Z

名称的格式不是标准化的,因编译器而异。最终结果是,除非您在C ++中编写另一个模块并使用相同的编译器对其进行编译,否则很难调用导出的C ++成员函数。尝试从Java调用比实现更麻烦。

相反,如果C ++库是您的,请使用extern "C"调用约定导出帮助函数:

Foo * foo;

extern "C"
{
  void myfunc()
  {
    foo->bar(); // Call C++ function within a C-style function.
  }
}

如果库是第三方,您应该使用自己的库包装它,该库使用C风格的导出公开必要的功能,如上所述。

答案 2 :(得分:0)

此错误通常表示库已成功加载, 但是功能的签名存在不一致。 在全球范围内,您的代码看起来是正确的,但我们确实添加了extern "C" 在我们的代码库中JNIEXPORT的前面。我们不使用生成的 标题,所以函数的定义是我们唯一可以做到的 指定extern "C"。在你的情况下,我认为编译器是 应该认识到在标题中声明的函数和 在.cpp中定义的一个是相同的,并将函数定义为 extern "C",但从未这样做,我不是百分百肯定。您 可以通过以下方式进行验证:

nm -C libxyz.so | egrep getAge

该函数应显示为C函数,第二个函数为T. 柱;没有-C,它应该看起来没有咒骂。请注意 定义和声明中最微小的差异就意味着 你正在定义一个不同的功能;我在代码中没有看到一个 你发布了,但值得仔细检查。

(我还会在try块中包含对LoadLibrary的调用,只是为了确定。)

已编辑以添加一些其他信息:

我不确定Linux何时以及如何链接所需的其他库; 我们总是明确地加载我们所有的库。你可以试试 在dlopen或者构造函数中添加对JNI_OnLoad的调用 静态对象。 (无论如何,我会推荐这个,以便控制 dlopen的参数。我们发现加载时有几种不同 .so,如果我们不这样做,RTTI在图书馆边界上不起作用。)I 不希望这样做会导致加载程序错误,但这一切都是如此 取决于Linux何时尝试加载libxyz.so;如果它只是这样做的话 JVM调用dlsym,然后你就会得到上面的错误。 (我认为这 取决于JVM在调用时传递给dlopen的参数 java.lang.System.LoadLibrary:如果它通过RTLD_LAZYRTLD_NOW。 通过强制JNI_OnLoad中的加载或静态的构造函数 对象,您或多或少保证加载器错误显示为加载器 错误,而不是链接错误,稍后。)