簡體   English   中英

在Linux上運行Java調用本機.so時發生永久性UnsatisfiedLinkError

[英]Persistent UnsatisfiedLinkError while running Java invoking native .so on Linux

我正在嘗試獲取一個小型/示例Java應用程序,該應用程序使用在Linux上運行的JNI調用來調用本機C ++代碼。

我使用Eclipse構建,配置環境並運行該應用程序。

我已將“總體”項目分為2個單獨的Eclipse項目:1個Java項目和1個C ++項目,其中包含本機代碼。

Eclipse項目視圖

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中的本機方法聲明以及適用的本機方法命名約定。 對我而言,似乎所有的構建/配置選項都已用盡-是否有人建議可能是什么原因,並且要解決此問題?

看來您的本機方法名稱無效。 確保正確逃脫_ _用於在本機方法中分隔軟件包。 您需要遵循命名約定:

https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names

如果方法名稱中包含“ _”,則需要在本機代碼中使用“ _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/

更新資料

您應該注意的地方:

  1. 如果其他所有操作均失敗,請確保在運行Eclipse之前設置LD_LIBRARY_PATH 這將為您提供一些有關Eclipse是否討厭或其他問題的見解。
  2. 啟動代碼時,請確保使用“ -D”傳遞java.library.path 您可以在Debug配置中將其設置為JVM參數
  3. 有時,這可能很棘手,並且可能導致您的lib根本不包含符號。 您可以使用nm檢查它

     nm libSomeLibFile.so 
  4. 您還可以在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.pathLD_LIBRARY_PATH指向該文件。 另一方面,如果使用System.load ,則無需對名稱進行任何假設。 只要確保您提供文件的完整路徑即可。 例如: /tmp/someFileWithMyLib

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM