[英]Exception in thread “main” java.lang.UnsatisfiedLinkError: Native.initiate(I)V while running native dll from Java
[英]Persistent UnsatisfiedLinkError while running Java invoking native .so on Linux
我正在尝试获取一个小型/示例Java应用程序,该应用程序使用在Linux上运行的JNI调用来调用本机C ++代码。
我使用Eclipse构建,配置环境并运行该应用程序。
我已将“总体”项目分为2个单独的Eclipse项目:1个Java项目和1个C ++项目,其中包含本机代码。
Java部分包括:1:一个“主”类,从中调用“ adapter”类以加载.so库并调用本机接口/ C ++方法2:一个“ adapter”类,其中包含本机方法声明
public class Java_Main_For_So_Kickoff {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello from Java main!");
Native_Cpp_Adapter adapter = new Native_Cpp_Adapter();
adapter.locSetProperty();
adapter.locLoadLib();
adapter.sayHello();
adapter.test_Kickoff_So_For_Print();
}
}
public class Native_Cpp_Adapter {
public native void test_Kickoff_So_For_Print();
public void locSetProperty() {
"/home/adminuser/workspace_Unit_Test_Java_Cpp/Unit_Test_Cpp/Debug");
String libPathProp = System.getProperty("java.library.path");
System.out.println("lib path set:" + libPathProp);
}
public void locLoadLib() {
System.setProperty("java.library.path",
"//home//adminuser//workspace_Unit_Test_Java_Cpp//Unit_Test_Cpp//Debug//");
String libPathProp = System.getProperty("java.library.path");
String soLibName = "libUnit_Test_Cpp.so";
String soLibWithPath = libPathProp.concat(soLibName);
System.out.println("lib path set for loading:" + libPathProp);
System.load(soLibWithPath);
System.out.println("library loaded");
}
public void sayHello() {
System.out.println("Hello from JNI Adapter (Java part)");
}
}
C ++部分包括:1:一个用javah生成的* .h文件,包含本机方法定义2:一个* .cpp文件,包含本机方法的C ++实现
两者都非常初级,仅意味着对JNI环境设置进行完整性测试。 添加了此内容,在此问题的原始帖子中省略了此内容。
.h文件:Native_Cpp_adapter.h
/*
* Native_Cpp_Adapter.h
*
* Created on: Aug 23, 2017
* Author: adminuser
*/
#ifndef SRC_NATIVE_CPP_ADAPTER_H_
#define SRC_NATIVE_CPP_ADAPTER_H_
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Native_Cpp_Adapter */
#ifndef _Included_Native_Cpp_Adapter
#define _Included_Native_Cpp_Adapter
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_Native_Cpp_Adapter_test_Kickoff_So_For_Print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
#endif /* SRC_NATIVE_CPP_ADAPTER_H_ */
.cpp文件:Native_Cpp_Adapter.cpp
#include "jni.h"
#include <iostream>
using namespace std;
JNIEXPORT void JNICALL Java_Native_Cpp_Adapter_test_Kickoff_So_For_Print
(JNIEnv *, jobject)
{
cout << "Correct kickoff of Native JNI method nr. 1";
return;
}
从C ++项目中构建了一个.so,包括jni.h和jni_md.h。 对于Java项目,生成的.so作为外部库包含在Java构建路径中,并作为VM参数“ java.library.path”添加到运行配置中(适用于Java项目/主类)。
首先,.so是从C ++项目构建的:这会在公共工作空间内的C ++项目文件夹中生成一个.so二进制文件。 Java构建已执行。 此后,将运行Java程序并调用项目的主类。
结果:.so似乎已加载,但是此后该过程中止,并出现(非常持久)“ UnsatisfiedLinkError”。
据我了解,如果Java虚拟机找不到声明为native的方法的适当的本地语言定义,则会引发此错误。
这听起来像本机方法定义(C ++端)与Java端的本机方法声明不兼容。 但是据我所知/知道,C ++定义完全符合Java中的本机方法声明以及适用的本机方法命名约定。 对我而言,似乎所有的构建/配置选项都已用尽-是否有人建议可能是什么原因,并且要解决此问题?
看来您的本机方法名称无效。 确保正确逃脱_ 。 _用于在本机方法中分隔软件包。 您需要遵循命名约定:
如果方法名称中包含“ _”,则需要在本机代码中使用“ _1”对其进行转义。
例。 对于Java中的方法:
public static native void display_Message();
你需要:
JNIEXPORT void JNICALL Java_recipeNo001_HelloWorld_display_1Message
(JNIEnv *, jclass);
注意在“ 显示 ”和“ 消息 ”之间的“ _1 ”。
从此处获取(并稍作修改)的来源: http : //jnicookbook.owsiak.org/recipe-No-001/
更新资料
您应该注意的地方:
LD_LIBRARY_PATH
。 这将为您提供一些有关Eclipse是否讨厌或其他问题的见解。 java.library.path
。 您可以在Debug配置中将其设置为JVM参数 有时,这可能很棘手,并且可能导致您的lib根本不包含符号。 您可以使用nm
检查它
nm libSomeLibFile.so
您还可以在Eclipse中的项目的Properties
配置中设置本机代码位置
更新。 我稍微简化了您的代码,使检查问题更容易。 我建议您从类名中删除“ _”,并将它们再次混入本机代码中。
在这里看看:
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello from Java main!");
NativeCppAdapter adapter = new NativeCppAdapter();
adapter.locLoadLib();
adapter.testKickoffSoForPrint();
}
}
本机适配器类
public class NativeCppAdapter {
public native void testKickoffSoForPrint();
public void locLoadLib() {
String soLibName = "/tmp/libNativeCppAdapter.so";
System.load(soLibName);
}
}
C ++代码(注意C导出-它会影响函数名称!)
#include "jni.h"
#include <iostream>
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: NativeCppAdapter
* Method: testKickoffSoForPrint
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeCppAdapter_testKickoffSoForPrint
(JNIEnv *, jobject) {
cout << "Correct kickoff of Native JNI method nr. 1";
return;
}
#ifdef __cplusplus
}
#endif
编译代码和Java
> javac *.java
> c++ -g -shared -fpic -I${JAVA_HOME}/include \
-I${JAVA_HOME}/include/darwin \
NativeCppAdapter.cpp \
-o /tmp/libNativeCppAdapter.so
> java -cp . Main
Hello from Java main!
Correct kickoff of Native JNI method nr. 1
再说几句话
如果编译代码时没有
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
您的库中的符号将不正确(不是JVM期望的)。
另一件事是。 如果使用“ loadLibrary”,则必须确保文件名的格式为:lib SomeName .so,然后通过System.loadLibrary("SomeName");
加载文件System.loadLibrary("SomeName");
-并且您必须确保java.library.path
或LD_LIBRARY_PATH
指向该文件。 另一方面,如果使用System.load
,则无需对名称进行任何假设。 只要确保您提供文件的完整路径即可。 例如: /tmp/someFileWithMyLib
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.