【已解决】JNA 回调函数工作不稳定现象处理


问题描述:

大概的描述下我的场景吧:

环境: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 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或者其他的版本就不稳定。

我现在也只是瞎猫碰上死耗子、如果有知道的小伙伴、欢迎指正、谢谢。

 

 

 

 

你可能感兴趣的:(【已解决】JNA 回调函数工作不稳定现象处理)