JNI 技术是双向的,既能够从Java 代码中调用原生函数,也能够从原生函数中直接建立
Java 虚拟机,并调用Java 代码。可是在原生函数中调用java代码要写大量C代码,这对大多数java程序员来讲是很头疼的。java
使用JNA,咱们不用编写C代码就能在原生代码中调用java代码。JNA 能够模拟函数指针,经过函数指针,就能够实如今原生代码中调用Java 函数。程序员
下面直接用代码进行说明:ide
原生代码定义:函数
//方法定义 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代码实现:.net
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 代码。code
代码说明:xml
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流写入具体结构体中,才能解析数据。所以,这里对结构体的定义要求必须彻底正确,即每一个字段的长度,字段的顺序都必须严格对应原生代码中的结构体,不然解析结果就会不正确。blog
欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/672662ip