我正在尝试获得一个小型/示例 Java 应用程序,它使用 JNI 调用调用 native C++ 代码,在 Linux 上运行。
我使用 Eclipse 构建、配置环境并运行应用程序。
我将“整体”项目分为 2 个独立的 Eclipse 项目:1 个 Java 项目和 1 个 C++ 项目,其中包含 native 代码。
Java部分包括: 1:一个“主”类,从中调用一个“适配器”类来加载.so库并调用 native 接口(interface)/C++方法 2:一个“适配器”类,包含本地方法声明
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:一个*.h文件,用javah生成,包含native方法定义 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++ 项目构建的:这会在公共(public)工作区内的 C++ 项目文件夹中生成一个 .so 二进制文件。 执行 Java 构建。此后运行 Java 程序并调用项目的主类。
结果:.so 似乎已加载,但此后该过程因(非常持久的)“UnsatisfiedLinkError”而中止。
据我所知,如果 Java 虚拟机找不到声明为 native 的方法的适当 native 语言定义,则会抛出此错误。
这听起来像是本地方法定义(C++ 端)与 Java 端的本地方法声明不兼容。 但据我所知,C++ 定义完全符合 Java 中的 native 方法声明以及适用的 native 方法命名约定。 对我来说,似乎所有构建/配置选项都已用尽 - 有没有人建议可能是什么原因,并解决这个问题?
最佳答案
您的 native 方法名称似乎无效。确保正确转义 _ 。 _ 用于分隔原生方法中的包。您需要遵循命名约定:
如果方法名称中有“_”,则需要在 native 代码中使用“_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/
更新
需要注意的地方:
- 如果一切都失败了,请确保在运行 Eclipse 之前设置
LD_LIBRARY_PATH
。这将使您了解 Eclipse 运行不正常还是其他什么东西坏了 - 确保在启动代码时使用“-D”传递
java.library.path
。您可以在调试配置中将其设置为 JVM 参数 有时,这可能很棘手,结果可能是您的库根本不包含符号。您可以使用
检查它nm
nm libSomeLibFile.so
您还可以在 Eclipse 中的项目的
Properties
配置中设置 native 代码位置
更新。我稍微简化了您的代码,以便更轻松地检查问题所在。我建议您从类名中删除“_”,因为它们会在 native 代码中再次混淆。
看这里:
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”,您必须确保文件名的格式为:libSomeName.so 并且您通过 System.loadLibrary("SomeName");
- 并且您必须确保 java.library.path
或 LD_LIBRARY_PATH
指向该文件。另一方面,如果您使用 System.load
,则不必对名称做出任何假设。只要确保您提供文件的完整路径即可。例如:/tmp/someFileWithMyLib
关于java - 在 Linux 上运行 Java 调用 native .so 时持续存在 UnsatisfiedLinkError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46527151/