文章开始之前首先介绍下.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的常用类型映射大家可以去网上搜索,这里就不一一罗列了。
本文暂时没有评论,来添加一个吧(●'◡'●)