问题描述:
大概的描述下我的场景吧:
环境:windows10 + idea + JNA-4.1.0
问题:发生在版本切换的时候、原先的功能时基于JNA4.1.0开发的。部署完成后,发现跟线上环境起冲突了(线上环境中有个JNA4.5.1的版本、本地是JNA4.1.0)
当切换版本后、发现功能流程是能正常跑通的,但是回调函数每次都是工作一段时间后就断开了、问题定位在回调函数那一块。
如果不愿意看过程的伙伴、可以直接看最下面、有一个错误示例和一个正确示例供参考
DLL中定义的文档信息
对应的JAVA类
结构体内嵌回调函数 =========================================================================================== PG_DEV_AUDIO_OUT_CALLBACK_S:音频输出回调接口结构 typedef struct tagPG_DEV_AUDIO_OUT_CALLBACK_S { // 打开音频播放设备 int (*pfnAudioOutOpen)(unsigned int uDevNO, unsigned int uSampleBits, unsigned int uSampleRate, unsigned int uChannels, unsigned int uPackBytes); // 关闭音频播放设备 void (*pfnAudioOutClose)(int iDevID); // 播放音频数据 int (*pfnAudioOutPlay)(int iDevID, const void* lpData, unsigned int uDataSize, unsigned int uFormat); } PG_DEV_AUDIO_OUT_CALLBACK_S; 具体的回调函数定义 ========================================================================================= 回调函数注册 void pgDevAudioOutSetCallback (const PG_DEV_AUDIO_IN_CALLBACK_S* lpstCallback); 音频开启 int (*pfnAudioOutOpen)(unsigned int uDevNO, unsigned int uSampleBits,unsigned int uSampleRate, unsigned int uChannels, unsigned int uPackBytes); 音频关闭 void (*pfnAudioOutClose)(int iDevID); 音频播放 int (*pfnAudioOutPlay)(int iDevID, const void* lpData,unsigned int uDataSize, unsigned int uFormat);
音频回调结构体 ========================================================================================= public class AudioCallBackStruct extends Structure { //Structure子类中的公共字段的顺序,必须与C语言中的结构的顺序一致,否则会报错! public PfnAudioOutOpen pfnAudioOutOpen; public PfnAudioOutClose pfnAudioOutClose; public PfnAudioOutPlay pfnAudioOutPlay; // getFiledOrder中添加String类型的字段、字段名称和顺序和C中定义的一致即可 @Override protected List getFieldOrder() { List<String> list = new ArrayList<>(); list.add("pfnAudioOutOpen"); list.add("pfnAudioOutClose"); list.add("pfnAudioOutPlay"); return list; } //结构体传指针 public static class ByReference extends AudioCallBackStruct implements Structure.ByReference { } //结构体传值 public static class ByValue extends AudioCallBackStruct implements Structure.ByValue{ } } 回调函数定义 ========================================================================================= // 关闭音频设备 public interface PfnAudioOutClose extends StdCallCallback { void pfnAudioOutClose(int iDevID); } // 打开音频播放设备 public interface PfnAudioOutOpen extends StdCallCallback { int pfnAudioOutOpen(int uDevNO, int uSampleBits, int uSampleRate, int uChannels, int uPackBytes); } // 播放音频数据 public interface PfnAudioOutPlay extends StdCallCallback { int pfnAudioOutPlay(int iDevID, Pointer lpData, int uDataSize, int uFormat); } 回调函数具体实现类 ========================================================================================= // 关闭音频设备实现类 public class PfnAudioOutCloseImpl implements PfnAudioOutClose { @Override public void pfnAudioOutClose(int iDevID) { VariateEnum.setDevId(-1); } } // 打开音频播放设备实现类 public class PfnAudioOutOpenImpl implements PfnAudioOutOpen { @Override public int pfnAudioOutOpen(int uDevNO, int uSampleBits, int uSampleRate, int uChannels, int uPackBytes) { try{ VariateEnum.getHandleThreadPool().submit(VariateEnum.getHandle()); VariateEnum.getReportThreadPool().submit(VariateEnum.getReport()); VariateEnum.setDevId(1234); return VariateEnum.getDevId(); }catch (Exception e){ } return -1; } } // 播放音频数据实现类 public class PfnAudioOutPlayImpl implements PfnAudioOutPlay { @Override public int pfnAudioOutPlay(int iDevID, Pointer lpData, int uDataSize, int uFormat) { try { VariateEnum.getAudioSizeQueue().offer(uDataSize); VariateEnum.getAudioDataQueue().offer(lpData.getByteArray(0, uDataSize)); }catch (Exception e){ } return uDataSize; } }
上面的是一些实现方式,接下来我们看下在调用时的信息
错误示例
这个是注册回调函数,从中可以看到对于结构体中的三个回调函数的实现。
但是问题就是在这个赋值上、因为这三个函数都是实时的被dll调用的、直接new出来的对象会产生工作不稳定的现象,具体原因还没了解(可能是因为GC的缘故?)
public class BaseDLL {
private static PfnAudioOutClose audioOutClose = new PfnAudioOutCloseImpl();
public static void pgDevAudioOutSetCallback(){
try{
AudioCallBackStruct.ByReference lpstCallback_out = new AudioCallBackStruct.ByReference();
lpstCallback_out.pfnAudioOutOpen = new PfnAudioOutPlayImpl();
lpstCallback_out.pfnAudioOutClose = new PfnAudioOutOpenImpl();
lpstCallback_out.pfnAudioOutPlay = new PfnAudioOutCloseImpl();
}catch (Exception e){
e.printStackTrace();
}
}
}
正确示例
正确的方式应该是对会被实时调用的函数、采用static方式、这样就能保证回调函数的稳定执行了
public class BaseDLL {
// 此处必须设为static、否则会被GC掉、导致回调函数只会工作片刻
private static PfnAudioOutPlay audioOutPlay = new PfnAudioOutPlayImpl();
private static PfnAudioOutOpen audioOutOpen = new PfnAudioOutOpenImpl();
private static PfnAudioOutClose audioOutClose = new PfnAudioOutCloseImpl();
public static void pgDevAudioOutSetCallback(){
try{
AudioCallBackStruct.ByReference lpstCallback_out = new AudioCallBackStruct.ByReference();
lpstCallback_out.pfnAudioOutOpen = audioOutOpen;
lpstCallback_out.pfnAudioOutClose = audioOutClose;
lpstCallback_out.pfnAudioOutPlay = audioOutPlay;
}catch (Exception e){
e.printStackTrace();
}
}
}
这个问题我也不大清楚为啥和版本有关、在JNA4.1.0中回调函数是稳定的、换成JNA4.5.1或者其他的版本就不稳定。
我现在也只是瞎猫碰上死耗子、如果有知道的小伙伴、欢迎指正、谢谢。