【已解决】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<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或者其他的版本就不稳定。

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

 

 

 

 

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
注:下文中的 *** 代表文件名中的版本号。 # 【jna-***.jar中文文档.zip】 中包含: 中文文档:【jna-***-javadoc-API文档-中文(简体)版.zip】 jar包下载地址:【jna-***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【jna-***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【jna-***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【jna-***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: jna-***.jar中文文档.zip,java,jna-***.jar,net.java.dev.jna,jna,***,com.sun.jna,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【jna-***.jar中文文档.zip】,再解压其中的 【jna-***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件; # Maven依赖: ``` <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>***</version> </dependency> ``` # Gradle依赖: ``` Gradle: implementation group: 'net.java.dev.jna', name: 'jna', version: '***' Gradle (Short): implementation 'net.java.dev.jna:jna:***' Gradle (Kotlin): implementation("net.java.dev.jna:jna:***") ``` # 含有的 Java package(包)(此处仅列举3个): ``` com.sun.jna com.sun.jna.internal com.sun.jna.platform ...... ``` # 含有的 Java class(类)(此处仅列举3个): ``` com.sun.jna.AltCallingConvention com.sun.jna.Callback com.sun.jna.Callback.UncaughtExceptionHandler ...... ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值