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