您的位置:首页 > 编程语言 > Java开发

JNA中级篇 回调函数详解

2016-05-10 00:00 183 查看
摘要: 本小节主要介绍dll回调JNA方法

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息