广告
返回顶部
首页 > 资讯 > 精选 >java高级用法之JNA中的回调问题怎么解决
  • 647
分享到

java高级用法之JNA中的回调问题怎么解决

2023-06-30 12:06:20 647人浏览 安东尼
摘要

今天小编给大家分享一下java高级用法之JNA中的回调问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。简介什么是c

今天小编给大家分享一下java高级用法之JNA中的回调问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

    简介

    什么是callback呢?简单点说callback就是回调通知,当我们需要在某个方法完成之后,或者某个事件触发之后,来通知进行某些特定的任务就需要用到callback了。

    最有可能看到callback的语言就是javascript了,基本上在javascript中,callback无处不在。为了解决callback导致的回调地狱的问题,es6中特意引入了promise来解决这个问题。

    为了方便和native方法进行交互,JNA中同样提供了Callback用来进行回调。JNA中回调的本质是一个指向native函数的指针,通过这个指针可以调用native函数中的方法,一起来看看吧。

    JNA中的Callback

    先看下JNA中Callback的定义:

    public interface Callback {    interface UncaughtExceptionHandler {        void uncaughtException(Callback c, Throwable e);    }    String METHOD_NAME = "callback";    List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(            Arrays.asList("hashCode", "equals", "toString"));}

    所有的Callback方法都需要实现这个Callback接口。Callback接口很简单,里面定义了一个interface和两个属性。

    先来看这个interface,interface名字叫做UncaughtExceptionHandler,里面有一个uncaughtException方法。这个interface主要用于处理JAVA的callback代码中没有捕获的异常。

    注意,在uncaughtException方法中,不能抛出异常,任何从这个方法抛出的异常都会被忽略。

    METHOD_NAME这个字段指定了Callback要调用的方法。

    如果Callback类中只定义了一个public的方法,那么默认callback方法就是这个方法。如果Callback类中定义了多个public方法,那么会选择METHOD_NAME = "callback"的这个方法作为callback。

    最后一个属性就是FORBIDDEN_NAMES。表示在这个列表里面的名字是不能作为callback方法使用的。

    目前看来是有三个方法名不能够被使用,分别是:“hashCode”, “equals”, “toString”。

    Callback还有一个同胞兄弟叫做DLLCallback,我们来看下DLLCallback的定义:

    public interface DLLCallback extends Callback {    @java.lang.annotation.Native    int DLL_FPTRS = 16;}

    DLLCallback主要是用在windows api的访问中。

    对于callback对象来说,需要我们自行负责对callback对象的释放工作。如果native代码尝试访问一个被回收的callback,那么有可能会导致VM崩溃。

    callback的应用

    callback的定义

    因为JNA中的callback实际上映射的是native中指向函数的指针。首先看一下在struct中定义的函数指针:

    struct _functions {  int (*open)(const char*,int);  int (*close)(int);};

    在这个结构体中,定义了两个函数指针,分别带两个参数和一个参数。

    对应的JNA的callback定义如下:

    public class Functions extends Structure {  public static interface OpenFunc extends Callback {    int invoke(String name, int options);  }  public static interface CloseFunc extends Callback {    int invoke(int fd);  }  public OpenFunc open;  public CloseFunc close;}

    我们在Structure里面定义两个接口继承自Callback,对应的接口中定义了相应的invoke方法。

    然后看一下具体的调用方式:

    Functions funcs = new Functions();lib.init(funcs);int fd = funcs.open.invoke("myfile", 0);funcs.close.invoke(fd);

    另外Callback还可以作为函数的返回值,如下所示:

    typedef void (*sig_t)(int);sig_t signal(int signal, sig_t sigfunc);

    对于这种单独存在的函数指针,我们需要自定义一个Library,并在其中定义对应的Callback,如下所示:

    public interface CLibrary extends Library {    public interface SignalFunction extends Callback {        void invoke(int signal);    }    SignalFunction signal(int signal, SignalFunction func);}

    callback的获取和应用

    如果callback是定义在Structure中的,那么可以在Structure进行初始化的时候自动实例化,然后只需要从Structure中访问对应的属性即可。

    如果callback定义是在一个普通的Library中的话,如下所示:

    public static interface TestLibrary extends Library {        interface VoidCallback extends Callback {            void callback();        }        interface ByteCallback extends Callback {            byte callback(byte arg, byte arg2);        }        void callVoidCallback(VoidCallback c);        byte callInt8Callback(ByteCallback c, byte arg, byte arg2);    }

    上例中,我们在一个Library中定义了两个callback,一个是无返回值的callback,一个是返回byte的callback。

    JNA提供了一个简单的工具类来帮助我们获取Callback,这个工具类就是CallbackReference,对应的方法是CallbackReference.getCallback,如下所示:

    Pointer p = new Pointer("MultiplyMappedCallback".hashCode());Callback cbV1 = CallbackReference.getCallback(TestLibrary.VoidCallback.class, p);Callback cbB1 = CallbackReference.getCallback(TestLibrary.ByteCallback.class, p);log.info("cbV1:{}",cbV1);log.info("cbB1:{}",cbB1);

    输出结果如下:

    INFO com.flydean.CallbackUsage - cbV1:Proxy interface to native function@0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$VoidCallback)
    INFO com.flydean.CallbackUsage - cbB1:Proxy interface to native function@0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$ByteCallback)

    可以看出,这两个Callback实际上是对native方法的代理。如果详细看getCallback的实现逻辑:

    private static Callback getCallback(Class<?> type, Pointer p, boolean direct) {        if (p == null) {            return null;        }        if (!type.isInterface())            throw new IllegalArgumentException("Callback type must be an interface");        Map<Callback, CallbackReference> map = direct ? directCallbackMap : callbackMap;        synchronized(pointerCallbackMap) {            Reference<Callback>[] array = pointerCallbackMap.get(p);            Callback cb = getTypeAssignableCallback(type, array);            if (cb != null) {                return cb;            }            cb = createCallback(type, p);            pointerCallbackMap.put(p, addCallbackToArray(cb,array));            // No CallbackReference for this callback            map.remove(cb);            return cb;        }    }

    可以看到它的实现逻辑是首先判断type是否是interface,如果不是interface则会报错。然后判断是否是direct mapping。实际上当前JNA的实现都是interface mapping,所以接下来的逻辑就是从pointerCallbackMap中获取函数指针对应的callback。然后按照传入的类型来查找具体的Callback。

    如果没有查找到,则创建一个新的callback,最后将这个新创建的存入pointerCallbackMap中。

    大家要注意, 这里有一个关键的参数叫做Pointer,实际使用的时候,需要传入指向真实naitve函数的指针。上面的例子中,为了简便起见,我们是自定义了一个Pointer,这个Pointer并没有太大的实际意义。

    如果真的要想在JNA中调用在TestLibrary中创建的两个call方法:callVoidCallback和callInt8Callback,首先需要加载对应的Library:

    TestLibrary lib = Native.load("testlib", TestLibrary.class);

    然后分别创建TestLibrary.VoidCallback和TestLibrary.ByteCallback的实例如下,首先看一下VoidCallback:

    final boolean[] voidCalled = { false };        TestLibrary.VoidCallback cb1 = new TestLibrary.VoidCallback() {            @Override            public void callback() {                voidCalled[0] = true;            }        };        lib.callVoidCallback(cb1);        assertTrue("Callback not called", voidCalled[0]);

    这里我们在callback中将voidCalled的值回写为true表示已经调用了callback方法。

    再看看带返回值的ByteCallback:

    final boolean[] int8Called = {false};        final byte[] cbArgs = { 0, 0 };        TestLibrary.ByteCallback cb2 = new TestLibrary.ByteCallback() {            @Override            public byte callback(byte arg, byte arg2) {                int8Called[0] = true;                cbArgs[0] = arg;                cbArgs[1] = arg2;                return (byte)(arg + arg2);            }        };final byte MAGIC = 0x11;byte value = lib.callInt8Callback(cb2, MAGIC, (byte)(MAGIC*2));

    我们直接在callback方法中返回要返回的byte值即可。

    多线程环境中使用callback

    默认情况下, callback方法是在当前的线程中执行的。如果希望callback方法是在另外的线程中执行,则可以创建一个CallbackThreadInitializer,指定daemon,detach,name,和threadGroup属性:

     final String tname = "VoidCallbackThreaded";        ThreadGroup testGroup = new ThreadGroup("Thread group for callVoidCallbackThreaded");        CallbackThreadInitializer init = new CallbackThreadInitializer(true, false, tname, testGroup);

    然后创建callback的实例:

    TestLibrary.VoidCallback cb = new TestLibrary.VoidCallback() {            @Override            public void callback() {                Thread thread = Thread.currentThread();                daemon[0] = thread.isDaemon();                name[0] = thread.getName();                group[0] = thread.getThreadGroup();                t[0] = thread;                if (thread.isAlive()) {                    alive[0] = true;                }                ++called[0];                if (THREAD_DETACH_BUG && called[0] == 2) {                    Native.detach(true);                }            }        };

    然后调用:

    Native.setCallbackThreadInitializer(cb, init);

    将callback和CallbackThreadInitializer进行关联。

    最后调用callback方法即可:

    lib.callVoidCallbackThreaded(cb, 2, 2000, "callVoidCallbackThreaded", 0);

    以上就是“java高级用法之JNA中的回调问题怎么解决”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

    --结束END--

    本文标题: java高级用法之JNA中的回调问题怎么解决

    本文链接: https://www.lsjlt.com/news/328838.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • java高级用法之JNA中的回调问题怎么解决
      今天小编给大家分享一下java高级用法之JNA中的回调问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。简介什么是c...
      99+
      2023-06-30
    • java高级用法之JNA中的回调问题
      目录简介JNA中的Callbackcallback的应用callback的定义callback的获取和应用在多线程环境中使用callback总结简介 什么是callback呢?简单点...
      99+
      2022-11-13
    • Java高级用法中的JNA类型映射注意细节及使用问题
      目录简介StringBuffers,Memory,数组和Pointer可变参数总结简介 JNA提供JAVA类型和native类型的映射关系,但是这一种映射关系只是一个大概的映射,我们...
      99+
      2022-11-13
    • Java循环内的回调函数问题怎么解决
      本篇内容介绍了“Java循环内的回调函数问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!问题出现在循环体内的回调函数,用一个很简单...
      99+
      2023-06-04
    • Java高级之HashMap中的entrySet()方法怎么使用
      本篇内容主要讲解“Java高级之HashMap中的entrySet()方法怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java高级之HashMap中的entrySet()方法怎么使用”...
      99+
      2023-07-05
    • C++回溯算法中的全排列问题怎么解决
      本文小编为大家详细介绍“C++回溯算法中的全排列问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++回溯算法中的全排列问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、全排列全排列的特点...
      99+
      2023-07-05
    • Java中Integer使用的问题怎么解决
      这篇“Java中Integer使用的问题怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java中Integer使用的...
      99+
      2023-07-04
    • 怎么使用Java递归回溯解决八皇后的问题
      这篇文章主要介绍“怎么使用Java递归回溯解决八皇后的问题”,在日常操作中,相信很多人在怎么使用Java递归回溯解决八皇后的问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用Java递归回溯解决八皇后...
      99+
      2023-06-25
    • C++回溯算法中组合的相关问题怎么解决
      这篇文章主要讲解了“C++回溯算法中组合的相关问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++回溯算法中组合的相关问题怎么解决”吧!回溯算法模板void backtracki...
      99+
      2023-07-05
    • Spring事务失效之怎么解决关于this调用的问题
      这篇文章主要介绍“Spring事务失效之怎么解决关于this调用的问题”,在日常操作中,相信很多人在Spring事务失效之怎么解决关于this调用的问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Sprin...
      99+
      2023-06-25
    • vue项目中created()被调用多次的问题怎么解决
      本文小编为大家详细介绍“vue项目中created()被调用多次的问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue项目中created()被调用多次的问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来...
      99+
      2023-07-05
    • Java多线程之常见锁策略与CAS中的ABA问题怎么解决
      本文小编为大家详细介绍“Java多线程之常见锁策略与CAS中的ABA问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java多线程之常见锁策略与CAS中的ABA问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一...
      99+
      2023-06-30
    • 怎么解决XP中启用Guest无法访问网络的问题
      本篇内容主要讲解“怎么解决XP中启用Guest无法访问网络的问题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么解决XP中启用Guest无法访问网络的问题”吧!好多Windows XP系统启用...
      99+
      2023-06-14
    • 旧项目升级新版Unity2021导致Visual Studio无法使用的问题怎么解决
      本篇文章为大家展示了旧项目升级新版Unity2021导致Visual Studio无法使用的问题怎么解决,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。在项目开发过程中,不可避免的会升级开发...
      99+
      2023-06-22
    • Java多线程run方法中直接调用service业务类应注意的问题及解决
      目录多线程run方法中直接调用service业务类应注意图解如下多线程知识点线程启动的四种方式使用@Aysnc注解实现多线程用户线程与守护线程的区别线程的六种状态Java锁的可重入性...
      99+
      2022-11-13
    • 怎么解决html5中的video标签ios系统中无法播放使用的问题
      这篇文章主要介绍怎么解决html5中的video标签ios系统中无法播放使用的问题,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!先是从前端的角度去考虑,如何让safari浏览器兼容video,并支持播放(未找到解决的...
      99+
      2023-06-09
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作