天天看點

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的常用類型映射大家可以去網上搜尋,這裡就不一一羅列了。