文章開始之前首先介紹下.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的常用類型映射大家可以去網上搜尋,這裡就不一一羅列了。