我正在尝试获取一个小型/示例Java应用程序,该应用程序使用在Linux上运行的JNI调用来调用本机C ++代码。
我使用Eclipse构建,配置环境并运行应用程序。
我在两个独立的Eclipse项目中划分了“整体”项目:1个Java项目和1个C ++项目,包含本机代码。
Java部分包括: 1:一个“main”类,从中调用“adapter”类来加载.so库并调用本机接口/ 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:使用javah生成的* .h文件,包含本机方法定义 2:a * .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中的本机方法声明,以及适用的本机方法命名约定。 对我来说,似乎所有的建筑/配置选项都已用尽 - 是否有人建议可能是什么原因,并解决这个问题?
答案 0 :(得分:0)
看起来您的原生方法的名称无效。确保正确逃脱 _ 。 _ 用于在本机方法中分隔包。您需要遵循命名约定:
如果你有&#34; _&#34;在方法名称中,您需要使用&#34; _1&#34;在本机代码中。
实施例。对于Java中的方法:
public static native void display_Message();
你需要:
JNIEXPORT void JNICALL Java_recipeNo001_HelloWorld_display_1Message
(JNIEnv *, jclass);
注意&#34; _1 &#34;介于&#34; 显示&#34;和&#34; 消息&#34;。
从此处获取(并稍加修改)来源:http://jnicookbook.owsiak.org/recipe-No-001/
<强>更新强>
你应该在哪里注意:
LD_LIBRARY_PATH
。这将为您提供一些洞察,让您了解Eclipse是否令人讨厌或其他东西是否已被破坏java.library.path
在启动代码时。您可以在调试配置中将其设置为JVM参数有时,它可能会很棘手,而且可能会发现你的lib根本不包含符号。您可以使用nm
nm libSomeLibFile.so
您还可以在Eclipse中的项目Properties
配置中设置本机代码位置
更新。我稍微简化了你的代码,以便更容易检查出错了什么。我建议你删除&#34; _&#34;从类名称以及它们再次以本机代码混合。
看看这里:
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();
}
}
Native Adapter类
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期望的那样)。
另一件事是。如果您使用&#34; loadLibrary&#34;您必须确保文件名的格式为:lib SomeName .so并且您通过System.loadLibrary("SomeName");
加载文件 - 您必须确保java.library.path
或{ {1}}指向该文件。另一方面,如果您使用LD_LIBRARY_PATH
,则不必对名称做出任何假设。只需确保提供完整的文件路径。例如:System.load