JNA中级篇 回调函数详解

萝らか妹 提交于 2019-12-04 11:13:06

        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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!