一、爲什麼要寫這篇文章
最近單位開發一些項目,要用到Java 調用 C/C++ 動態鏈接庫,我採了JNA的調用方式,同時在開發過程中遇到過一些問題,但上網查找,發現對JNA的介紹不多,大部分只是一個初級入門,所以結合自己的實際開發,把總結出來的知識點及問題與大家分享下。在Windows 平臺與 Linux平臺都實現了。
二、爲什麼選用JNA
Java 調用 C/C++ 動態鏈接庫,有兩種方式 JNI 與 JNA,它們的原理一樣,但使用起來難易度卻有區別,做個對比:
1. JNI 比JNA難度要高一些,因爲JNI要根據Java代碼生成頭文件,生成特殊的方法簽名。再針對頭文件定義的函數,用JNI 語法結合C/C++語法來實現函數,在函數中再去調用動態鏈接庫,在這個過程中還需要去管理內存的問題(有一篇文章給大家推薦下:在 JNI 編程中避免內存泄漏)。而JNA方式完全省略了這一複雜過程。使用方便是JNA的一大特點。
2. JNI 可以實現Java 調用 C/C++,同時 C/C++也可調用Java 。但JAN卻只能Java 調用 C/C++,不具備反向調用。剛好在項目中不要用到C/C++調用JAVA,所以選擇了JNA的方式。
三、 環境申明
環境沒有配好,會出現許多錯誤,如:找不到庫文件,無法加載庫文件等等,如果是剛上手,一時會找不到出錯點。
我實際項目中用到的環境是(已經設置好了jdk的環境,另外在Linux平臺上還要加設一變量LD_LIBRARY_PATH,它的值要等於你即將調用的so文件存放的目錄,我習慣把so文件存在我的項目直接目錄下,路徑也就指向項目目錄):
1. windows (64位), JDK (64位),dll文件 (64位)
2. Linux (64位), JDK (64位),so文件 (64位)
我的意思是要用 64位的JDK去調用64位的dll/so 文件; 32位的JDK去調用32位的dll/so 文件,如果不對應則會生如下異常信息:
異常一:如在windows平臺,用64位jdk 調用 32位的dll :Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'XxxEngine': Native library (win32- x86-64/XxxEngine.dll) not found in resource path,它是意思是沒有找到64位的dll文件,它需要的是64的文件。
異常二:我在Linux平臺上還發現有這樣一個異常:Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'XxxEngine': Native library (linux-x86-64/libKwsEngine.so) not found in resource path,而我已經保證了64位的JDK調用64位的so 文件,並且Linux也是64位的操作系統,經過一番查找,是因爲libXxxEngine.so文件內部引用了另外的so文件,把的依賴的文件加入就可以解決問題了,如放在/usr/local/lib 下面,此爲系統默認查找庫資源的路徑。如何查看一個so文件依賴其它的文件方式是:ldd libXxxEngine.so。如果確定環境不再有問題,也執行過ldconfig命令,則重新啓動下電腦試試。
三、官方JNA
JNA的官方資源路徑爲https://github.com/twall/jna/,在這裏可以下載 jna.jar 文件,以及可以查看文檔JavaDoc,開發時一定要查看文檔,因以它裏面介紹了C Type 對應到 Java Type 數據類型。如下圖
四、開發結構
1. 把需要調用的dll/so文件放到項目的根目錄下,如下圖:在linux環境下面libTestCF.so (在linux 下面要設置環境變量:LD_LIBRARY_PATH=/root/workspace/HelloJNA , Windows下面不用設置此環境變量),在linux下的文件爲libTestCF.so,在windows下面爲TestCF.dll
2. 在libTestCF.so有要被調用的函數,在cf.h頭文件有如下申明:採用C語言形式接口函數
- extern "C"
- {
- Public int TestReadCF();
- }
Java代碼爲:
- package cn.jna.test;
- import com.sun.jna.Library;
- import com.sun.jna.Native;
- public interface CFJna extends Library {
- //加載動態資源庫
- CFJna library = (CFJna) Native.loadLibrary("TestCF", CFJna.class);//注意庫名稱的寫法不要含有lib字符
- //定義要調用的方法,與cf.h頭文件中定義的函數名一樣
- int TestReadCF();
- }
4. 測試調用,如下圖:
java 代碼:
- package cn.jna.test;
- public class CFJnaTest {
- public static void main(String[] args) {
- int testReadCF = CFJna.library.TestReadCF();
- System.out.println("testReadCF:"+testReadCF);
- }
- }