我在从JNI调用C函数时遇到UnsatisfiedLinkError,虽然我的设置似乎是正确的。这就是我所做的:
有一个Java类:
package com.mycompany.myproduct;
public class Foo {
static {
System.loadLibrary("external");
}
public void native do_foo();
}
我已将libexternal.so
放置到LD_LIBRARY_PATH
,编译了该类,并对其执行了javah
。生成的com_mycompany_myproduct_Foo.h
文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_mycompany_myproduct_Foo */
#ifndef _Included_com_mycompany_myproduct_Foo
#define _Included_com_mycompany_myproduct_Foo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_com_mycompany_myproduct_Foo
* Method: do_foo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_mycompany_myproduct_Foo_do_1foo(JNIEnv *, jobject);
在ctinative.c
中实施了C委托(不确定是否需要extern "C"
):
#include "com_mycompany_myproduct_Foo.h"
#include "External.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_com_mycompany_myproduct_Foo
* Method: do_foo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_mycompany_myproduct_Foo_do_1foo(JNIEnv *, jobject) {
do_foo(); // this is a function that defined in External.h
}
#ifdef __cplusplus
}
#endif
编译并得到ctinative.o
:
gcc -x c -g -m64 -DUNIX=1 -DUSE_SBUF=1 -DMAIN_VERSION=0 -DC_VER=7 -I$(EXTERNAL_SDK_ROOT)/include -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -o ctinative.o -c ctinative.c
以下是nm ctinative.o
的输出(U
正常吗?):
0000000000000000 T Java_com_mycompany_myproduct_Foo_do_1foo
U do_foo
将ctinative.o
置于LD_LIBRARY_PATH
。现在,在调用Foo.do_foo()
时,我收到了UnsatisfiedLinkError:
java.lang.UnsatisfiedLinkError: com.mycompany.myproduct.Foo.do_foo()V
at com.mycompany.myproduct.Foo.do_foo(Native Method)
如果我从ctinative.o
删除LD_LIBRARY_PATH
,则错误不会更改。如果我从libexternal.so
删除LD_LIBRARY_PATH
,那么我当然会得到:
java.lang.UnsatisfiedLinkError: no external in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1734)
at java.lang.Runtime.loadLibrary0(Runtime.java:823)
at java.lang.System.loadLibrary(System.java:1028)
at com.mycompany.myproduct.Foo.<clinit>
对我做错了什么的想法?
答案 0 :(得分:2)
好吧,我在Linux上使用本机库的经验仅限于玩具测试,但我在Windows上使用它们非常广泛。我希望机制类似,但要谨慎行事:)
当您执行方法Java_com_mycompany_myproduct_Foo_do_1foo()
时,Java最终会调用fooInstance.do_foo()
本机函数。这是需要在libexternal.so
中定义的本机函数(或者您选择使用loadLibrary()
加载的任何内容)。
如果我正确理解了您的问题,您已将函数Java_com_mycompany_myproduct_Foo_do_1foo()
编译为ctinative.o
,并且该实现未显示在libexternal.so
中。您可以使用objdump --dynamic-reloc libexternal.so
进行检查。
我认为您需要将Java_com_mycompany_myproduct_Foo_do_1foo()
的原生实施汇编到libexternal.so
,或者您可以链接ctinative.o
以生成类似libctinative.so
的动态链接库。
编辑:要加入点,完整的机制将是:
loadLibrary()
的{{1}}文件上调用.so
。我们称之为Java_com_mycompany_myproduct_Foo_do_1foo()
。libctinative.so
通过O / S的动态链接机制动态加载libctinative.so
- 除了编译和链接libexternal.so
之外,您不需要做任何特殊的事情来实现这一点正确的方式答案 1 :(得分:0)
您有Java_com_mycompany_myproduct_Foo_do_1foo()但是本机void do_foo()。生成.h / .c文件时,do_1foo()是否为其名称?如果你改变它,你必须重新生成。