[英]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.