文章開始之前首先介紹下.dll/.so文件,我們知道用c/c++編寫的程序如果用於Windows平臺則編譯爲xxx.dll(dynamic link library)文件,Linux平臺則編譯爲libxxx.so(shared object)文件。像在Android移動應用開發過程中,就經常調用動態鏈接庫(.so文件),由於Android系統是基於Linux內核的,所以工程中引用的是.so文件。另外與一些網絡設備交互控制,也需要訪問廠家提供的動態庫(.dll/.so文件)。作者最近一個項目是把網絡攝像機(IPC)及對應的網絡硬盤錄像機(NVR)接入自己平臺中,從而實現對網絡設備控制和監聽,這就必然需要調用dll/so實現控制和監聽功能。
JNA(Java Native Access)框架是一個開源的Java框架,是SUN公司主導開發的,建立在經典的JNI的基礎之上的一個框架。相比於JNI,JNA可以更方便地調用.dll/.so動態庫。
使用JNA首先根據頭文件聲明接口,然後利用代理生成接口實例,接下來就可以直接使用接口實例進行接口調用。當然這種好用的模式需要你下載jna.jar這個包加入你工程中,說穿了JNA已經爲你做了很多轉換的工作。
NetSDKLib NETSDK_INSTANCE = (NetSDKLib)Native.loadLibrary(Utils.getLoadLibrary("dhnetsdk"), NetSDKLib.class);
上面語句中dhnetsdk就代表需要引用的.dll/.so文件的名稱,由於java是跨平臺,所以不加後綴,實際文件名爲dhnetsdk.dll/libdhnetsdk.so
下面以網絡攝像機抓圖圖片爲例,寫一個簡單的JNA使用Demo。
一、創建一個接口並繼承於com.sun.jna.Library
本示例中加載的動態庫爲libdhnetsdk.so(Windows環境下直接放入工程目錄下即可,Linux環境放入/lib或者usr/lib即可),實際工程中需要聲明的接口很多,這裏只列出了幾個重要接口。
public interface NetSDKLib extends Library {
NetSDKLib NETSDK_INSTANCE = (NetSDKLib)Native.loadLibrary(Utils.getLoadLibrary("dhnetsdk"), NetSDKLib.class);
// JNA直接調用方法定義,登陸擴展接口
public LLong CLIENT_LoginEx2(String pchDVRIP, int wDVRPort, String pchUserName, String pchPassword, int nSpecCap, Pointer pCapParam, NET_DEVICEINFO_Ex lpDeviceInfo, IntByReference error);
// JNA直接調用方法定義,cbDisConnect 實際情況並不回調Java代碼,僅爲定義可以使用如下方式進行定義。 fDisConnect 回調
public boolean CLIENT_Init(StdCallCallback cbDisConnect, Pointer dwUser);
// 打開日誌功能
public boolean CLIENT_LogOpen(LOG_SET_PRINT_INFO pstLogPrintInfo);
// 抓圖請求擴展接口
public boolean CLIENT_SnapPictureEx(LLong lLoginID, SNAP_PARAMS stParam, IntByReference reserved);
// 設置抓圖回調函數, fSnapRev回調
public void CLIENT_SetSnapRevCallBack(StdCallCallback OnSnapRevMessage, Pointer dwUser);
}
二、應用程序中實際調用
正式開始抓取圖片前,需要進行一些初始化工作,像設備初始化、設備斷開監聽、設備重連監聽以及設置抓圖回調,一切準備就緒後就可以主動遠程抓圖了。
// 設備初始化
LoginModule.init(disConnectCallback, null);
// 設置抓圖回調
LoginModule.setSnapRevCallBack(captureCallback);
// 遠程抓圖
LoginModule.snapPicture(deviceInfo.getLoginHandle(), deviceInfo.getChannel());
public class LoginModule {
public static NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
/**
* 遠程抓圖
*/
public static boolean snapPicture(LLong m_hLoginHandle, int chn) {
// 發送抓圖命令給前端設備,抓圖的信息
NetSDKLib.SNAP_PARAMS msg = new NetSDKLib.SNAP_PARAMS();
msg.Channel = chn; // 抓圖通道
msg.mode = 0; // 抓圖模式
msg.Quality = 3; // 畫質
msg.InterSnap = 0; // 定時抓圖時間間隔
IntByReference reserved = new IntByReference(0);
if (!LoginModule.netsdk.CLIENT_SnapPictureEx(m_hLoginHandle, msg, reserved)) {
System.err.printf("SnapPictureEx Failed!" + ToolKits.getErrorCodePrint());
return false;
} else {
System.out.println("SnapPictureEx success");
}
return true;
}
}
三、數據類型轉換
JNA的一個難點就是數據類型轉換,這也是跨平臺/語言調用的共同問題,不同語言之間的數據類型不一致。絕大部分跨平臺調用的失敗,都是這個問題造成的。上面說到接口中使用的函數必須與動態庫中的函數原型保持一致,由於C/C++類型與Java的類型是不一樣的,所以必須轉換類型讓它們保持一致。JNA的常用類型映射大家可以去網上搜索,這裏就不一一羅列了。