您好,欢迎访问一九零五行业门户网

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中的回调问题怎么解决的详细内容。
其它类似信息

推荐信息