JNA中级篇 回调函数详解
2016-05-10 00:00
183 查看
摘要: 本小节主要介绍dll回调JNA方法
JNI 技术是双向的,既可以从Java 代码中调用原生函数,也可以从原生函数中直接创建
Java 虚拟机,并调用Java 代码。但是在原生函数中调用java代码要写大量C代码,这对大多数java程序员来说是很头疼的。
使用JNA,我们不用编写C代码就能在原生代码中调用java代码。JNA 可以模拟函数指针,通过函数指针,就可以实现在原生代码中调用Java 函数。
下面直接用代码进行说明:
原生代码定义:
java代码实现:
回调函数实现类:
原生函数可以通过函数指针实现函数回调,调用外部函数来执行任务。JNA 可以方便地模拟函数指针,把Java 函数作为函数指针传递给原生函数,实现在原生代码中调用Java 代码。
代码说明:
alarmMsg.alarmInfo在结构体中是指针类型,指向的内容根据dwType不同而不同,由于Pointer指向的是内存块,因此需要将内存中的数据转为byte流写入具体结构体中,才能解析数据。因此,这里对结构体的定义要求必须完全正确,即每个字段的长度,字段的顺序都必须严格对应原生代码中的结构体,否则解析结果就会不正确。
欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/672662
JNI 技术是双向的,既可以从Java 代码中调用原生函数,也可以从原生函数中直接创建
Java 虚拟机,并调用Java 代码。但是在原生函数中调用java代码要写大量C代码,这对大多数java程序员来说是很头疼的。
使用JNA,我们不用编写C代码就能在原生代码中调用java代码。JNA 可以模拟函数指针,通过函数指针,就可以实现在原生代码中调用Java 函数。
下面直接用代码进行说明:
原生代码定义:
//方法定义 LONG StartListenServer(const Alarm_Listen_Param *param); //Alarm_Listen_Param结构体 struct{ IpAddress struIPAdress; MessageCallBack fnMsgCb; void *pUserData; BYTE byProtocolType; BYTE byRes[31]; }Alarm_Listen_Param, *Alarm_Listen_Param; //ip结构体 struct{ char szIP[128]; WORD wPort; BYTE byRes[2]; }IpAddress, *IpAddress; //回调函数声明 typedef BOOL (CALLBACK *MessageCallBack)( LONG iHandle, AlarmMessage *pAlarmMsg, void *pUserData ); //启动参数结构体 struct{ DWORD alarmType; void *alarmInfo; DWORD alarmInfoLen; void *pXmlBuf; DWORD xmlBufLen; BYTE byRes[32]; }AlarmMessage, *AlarmMessage; //返回值结构体 struct{ DWORD dwSize; char alarmTime[32]; char deviceID[256]; DWORD alarmType; DWORD alarmAction; DWORD videoChannel; DWORD alarmInChannel; DWORD diskNumber; BYTE remark[64]; BYTE retransFlag; BYTE byRes[63]; }AlarmInfo,*AlarmInfo;
java代码实现:
public interface AlarmServer extends StdCallLibrary{ public static final int UNKNOWN =0; public static final int ALARM =1; public static final int REPORT =3; public final static int MAX_DEVICE_ID_LEN =256; public final static int MAX_TIME_LEN =32; public final static int MAX_REMARK_LEN =64; //创建唯一实例 AlarmServer INSTANCE=(AlarmServer ) Native.loadLibrary("AlarmServer",AlarmServer .class); //启动参数结构体 public static class Alarm_Listen_Param extends Structure{ public IpAddress struIPAdress; public MessageCallBack fnMsgCb;//回调函数 public Pointer pUserData; public byte byProtocolType; public byte[] byRes=new byte[31]; } //ip结构体 public static class IpAddress extends Structure{ public byte[] ip=new byte[128]; public short port; public byte[] byRes=new byte[2]; } //回调函数参数结构体 public static class AlarmMessage extends Structure{ public int alarmType;//类型 public Pointer alarmInfo;//内容 public int alarmInfoLen;//缓冲区大小 public String xmlBuf;//内容(XML) public int xmlBufLen;//内容大小 public byte[] byRes=new byte[20]; } //返回值结构体 public static class AlarmInfo extends Structure{ public int size; public byte[] alarmTime=new byte[MAX_TIME_LEN]; public byte[] deviceID=new byte[MAX_DEVICE_ID_LEN]; public int alarmType; public int alarmAction; public int videoChannel; public int alarmInChannel; public int diskNumber; public byte[] remark=new byte[MAX_REMARK_LEN]; public byte retransFlag; public byte[] byRes=new byte[63]; } //回调函数定义 public static interface MessageCallBack extends StdCallCallback{ public boolean invoke(NativeLong iHandle, AlarmMessage pAlarmMsg,Pointer pUserData); } }
回调函数实现类:
//回调函数具体实现类,处理业务逻辑 public class AlarmServerImpl { public static class MessageCallBackImpl implements MessageCallBack{ public boolean invoke(NativeLong iHandle, AlarmMessage alarmMsg,Pointer pUserData){ boolean isSuccess=false; try{ int dwType=alarmMsg.alarmType; switch(dwType){ case AlarmServer.UNKNOWN: System.out.println("未知类型"); break; case AlarmServer.ALARM: AlarmInfo alarmInfo=new AlarmInfo(); alarmInfo.write(); Pointer p=alarmInfo.getPointer(); p.write(0,alarmMsg.alarmInfo.getByteArray(0, alarmMsg.alarmInfoLen),0, alarmMsg.alarmInfoLen); alarmInfo.read(); System.out.println("设备id="+newString(alarmInfo.deviceID).trim()); System.out.println("报警内容="+alarmMsg.xmlBuf); //...具体业务逻辑 case AlarmServer.REPORT: //...具体业务逻辑 break; default: break; } isSuccess=true; }catch(Exception e){ e.printStackTrace(); } return isSuccess; } } }
原生函数可以通过函数指针实现函数回调,调用外部函数来执行任务。JNA 可以方便地模拟函数指针,把Java 函数作为函数指针传递给原生函数,实现在原生代码中调用Java 代码。
代码说明:
AlarmInfo alarmInfo=new AlarmInfo(); alarmInfo.write(); Pointer p=alarmInfo.getPointer(); p.write(0,alarmMsg.alarmInfo.getByteArray(0,alarmMsg.alarmInfoLen),0, alarmMsg.alarmInfoLen); alarmInfo.read();
alarmMsg.alarmInfo在结构体中是指针类型,指向的内容根据dwType不同而不同,由于Pointer指向的是内存块,因此需要将内存中的数据转为byte流写入具体结构体中,才能解析数据。因此,这里对结构体的定义要求必须完全正确,即每个字段的长度,字段的顺序都必须严格对应原生代码中的结构体,否则解析结果就会不正确。
欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/672662
相关文章推荐
- java使用JNA(Java Native Access)调用dll的方法
- Java调用c++库
- JNA 基础篇<一> 初识JNA
- JNA 基础篇<二> 结构体
- Java调用C语言Dll库回调函数
- JNA异常java.lang.UnsatisfiedLinkError: Unable to load library
- JNA调用C语言动态链接库学习实践总结(指针模拟)
- JAVA中的jna
- 关于JNA、JNI的使用注意
- JNA回调线程的类加载器(ClassLoader)为空
- Android Studio里使用JNA
- JNA--JNI终结者
- JNA操作windows 剪切板并生成emf图片
- JNI的替代者—使用JNA访问Java外部函数接口
- 关于JNA调用32位和64位动态链接库,即*.dll
- 【CSDN常见问题解答】使用JNA调用Windows动态库
- JNA使用问题及解决
- JAVA调用动态链接库
- JNA调用C动态库dll、so
- LINUX 下 JNA 调用 so--正确版