写点什么

java 高级用法之:JNA 中的 Function

作者:程序那些事
  • 2022 年 4 月 15 日
  • 本文字数:2034 字

    阅读完需:约 7 分钟

java高级用法之:JNA中的Function

简介

在 JNA 中,为了和 native 的 function 进行映射,我们可以有两种 mapping 方式,第一种是 interface mapping,第二种是 direct mapping。虽然两种方式不同,但是在具体的方法映射中,我们都需要在 JAVA 中定义一个和 native 方法进行映射的方法。


而这个 JAVA 中的映射在 JNA 中就是一个 function。通过或者 function 对象,我们可以实现一些非常强大的功能,一起看看吧。

function 的定义

先来看下 JNA 中 Function 的定义:


public class Function extends Pointer
复制代码


可以看到 Function 实际上是一个 Pointer,指向的是 native function 的指针。


那么怎么得到一个 Function 的实例呢?


我们知道 JNA 的流程是先进行 Library 的映射,然后再对 Library 中的 Function 进行映射。所以很自然的我们应该可以从 Library 中得到 Function。


我们看一下根据 Library name 得到 function 实例的方法定义:


public static Function getFunction(String libraryName, String functionName, int callFlags, String encoding) {        return NativeLibrary.getInstance(libraryName).getFunction(functionName, callFlags, encoding);    }
复制代码


这个方法可以接受 4 个参数,前面两个参数大家应该很熟悉了,第三个参数是 callFlags,表示的是函数调用的 flags,Function 定义了三个 callFlags:


    public static final int C_CONVENTION = 0;
public static final int ALT_CONVENTION = 0x3F;
public static final int THROW_LAST_ERROR = 0x40;
复制代码


其中 C_CONVENTION 表示的是 C 语言类型的方法调用。


ALT_CONVENTION 表示的其他的调用方式。


THROW_LAST_ERROR 表示如果 native 函数的返回值是非零值的时候,将会抛出一个 LastErrorException。


最后一个参数是 encoding,表示的是字符串的编码方式,实际上指的是 Java unicode 和 native (const char*) strings 的转换方式。


除了根据 Library name 获取 Function 之外,JNA 还提供了根据 Pointer 来获取 Function 的方法。


    public static Function getFunction(Pointer p, int callFlags, String encoding) {        return new Function(p, callFlags, encoding);    }
复制代码


这里的 Pointer 指的是一个执行 native 方法的指针,因为 Function 本身就是继承自 Pointer。所以跟 Pointer 来创建 Function 的本质就是在 Pointer 的基础上添加了一些 Function 特有的属性。


有了 Function 的定义,更为重要的是如何通过 Function 来调用对应的方法。跟反射很类似,Function 中也有一个 invoke 方法,通过调用 invoke,我们就可以执行对应的 Function 的功能。


Function 中的 invoke 方法有两种,一种是通用的返回对象 Object,一种是带有返回值的 invoke 方法,比如 invokeString,invokePointer,invokeInt 等。

Function 的实际应用

Function 的实际使用和 JAVA 中的反射有点类似,其工作流程是首先获得要加载的 NativeLibrary,然后从该 NativeLibrary 中找到要调用的 Function,最后 invoke 该 Function 的某些方法。


C 语言中的 printf 应该是大家最熟悉的 native 方法了。我们看一下如何使用 Function 来调用这个方法吧:


        NativeLibrary lib = NativeLibrary.getInstance(Platform.C_LIBRARY_NAME);        Function f = lib.getFunction("printf");        try {            f.invoke(getClass(), new Object[] { getName() });            fail("Invalid return types should throw an exception");        } catch(IllegalArgumentException e) {            // expected        }
复制代码


可以看到调用的流程非常简洁。如果是用 interface Mapping 或者 direct Mapping 的形式,我们还需要自定义一个 interface 或者 class,并且在其中定义一个相应的 java 方法映射。但是如果使用 Function 的话,这些都不需要了。我们直接可以从 NativeLibrary 中拿到对应的函数,并最终调用其中的方法。


C 语言中的 printf 的原型如下:


# include <stdio.h>int printf(const char *format, ...);
复制代码


printf 带有返回值的,如果要输出这个返回值,则可以调用 Function 中的 invokeInt 命令。我们再来看一个有返回值的调用例子:


NativeLibrary lib = NativeLibrary.getInstance(Platform.C_LIBRARY_NAME);        Function f = lib.getFunction("printf");        Object[] args = new Object[Function.MAX_NARGS+1];        // Make sure we don't break 'printf'        args[0] = getName();        try {            f.invokeInt(args);            fail("Arguments should be limited to " + Function.MAX_NARGS);        } catch(UnsupportedOperationException e) {            // expected        }
复制代码

总结

使用 Function 可以减少手写 Mapping 的工作,在某些情况下是非常好用的,但是 Function 的 invoke 支持 TypeMapper,并不支持 FunctionMapper,所以在使用中还是有一些限制。


大家可以在使用过程中酌情考虑。


本文已收录于 http://www.flydean.com/07-jna-function/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 刚刚阅读数: 2
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
java高级用法之:JNA中的Function_Java_程序那些事_InfoQ写作平台