JNA中级篇 回调函数详解

        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

相关文章
相关标签/搜索