JNA實戰筆錄

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章