写点什么

java 高级用法之: 在 JNA 中使用类型映射

作者:程序那些事
  • 2022 年 3 月 28 日
  • 本文字数:2568 字

    阅读完需:约 8 分钟

java高级用法之:在JNA中使用类型映射

简介

JNA 中有很多种映射,library 的映射,函数的映射还有函数参数和返回值的映射,libary 和函数的映射比较简单,我们在之前的文章中已经讲解过了,对于类型映射来说,因为 JAVA 中的类型种类比较多,所以这里我们将 JNA 的类型映射提取出来单独讲解。

类型映射的本质

我们之前提到在 JNA 中有两种方法来映射 JAVA 中的方法和 native libary 中的方法,一种方法叫做 interface mapping,一种方式叫做 direct mapping。


但是我们有没有考虑过这两种映射的本质是什么呢?


比如 native 有一个方法,我们是如何将 JAVA 代码中的方法参数传递给 native 方法,并且将 native 方法的返回值转换成 JAVA 中函数的返回类型呢?


答案就是序列化。


因为本质上一切的交互都是二进制的交互。JAVA 类型和 native 类型进行转换,最简单的情况就是 JAVA 类型和 native 类型底层的数据长度保持一致,这样在进行数据转换的时候就会更加简单。


我们看下 JAVA 类型和 native 类型的映射和长度关系:



上面的 JAVA 类型都是 JDK 自带的类型(Pointer 除外)。


除了 JAVA 自带的类型映射,JNA 内部也定义了一些数据类型,可以跟 native 的类型进行映射:


TypeMapper

除了定义好的映射关系之外,大家也可以使用 TypeMapper 来对参数类型进行自定义转换,先来看下 TypeMapper 的定义:


public interface TypeMapper {
FromNativeConverter getFromNativeConverter(Class<?> javaType);
ToNativeConverter getToNativeConverter(Class<?> javaType);}
复制代码


TypeMapper 是一个 interface,它定义了两个 converter 方法,分别是 getFromNativeConverter 和 getToNativeConverter。


如果要使用 TypeMapper 则需要实现它而这两个方法即可。我们看一下官方的 W32APITypeMapper 是怎么实现的:


 TypeConverter stringConverter = new TypeConverter() {                @Override                public Object toNative(Object value, ToNativeContext context) {                    if (value == null)                        return null;                    if (value instanceof String[]) {                        return new StringArray((String[])value, true);                    }                    return new WString(value.toString());                }                @Override                public Object fromNative(Object value, FromNativeContext context) {                    if (value == null)                        return null;                    return value.toString();                }                @Override                public Class<?> nativeType() {                    return WString.class;                }            };            addTypeConverter(String.class, stringConverter);            addToNativeConverter(String[].class, stringConverter);
复制代码


首先定义一个 TypeConverter,在 TypeConverter 中实现了 toNative,fromNative 和 nativeType 三个方法。在这个例子中,native type 是 WString,而 JAVA type 是 String。而这个 TypeConverter 就是最终要使用的 FromNativeConverter 和 ToNativeConverter。


有了 typeMapper,应该怎么使用呢?最简单的方法就是将其添加到 Native.load 的第三个参数中,如下所示:


 TestLibrary lib = Native.load("testlib", TestLibrary.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, mapper));
复制代码

NativeMapped

TypeMapper 需要在调用 Native.load 方法的时候传入,从而提供 JAVA 类型和 native 类型的转换关系。TypeMapper 可以看做是类型转换关系的外部维护者。


可能很多朋友已经想到了,既然能在 JAVA 类型外部维护转换关系,那么可不可以在 JAVA 类型本身对这个转换关系进行维护呢?答案是肯定的,我们只需要在要实现转换类型关系的 JAVA 类型实现 NativeMapped 接口即可。


先来看下 NativeMapped 接口的定义:


public interface NativeMapped {
Object fromNative(Object nativeValue, FromNativeContext context);
Object toNative();
Class<?> nativeType();}
复制代码


可以看到 NativeMapped 中定义要实现的方法基本上和 FromNativeConverter、ToNativeConverter 中定义的方法一致。


下面举一个具体的例子来说明一下 NativeMapped 到底应该怎么使用。首先我们定义一个 enum 类实现 NativeMapped 接口:


    public enum TestEnum implements NativeMapped {        VALUE1, VALUE2;
@Override public Object fromNative(Object nativeValue, FromNativeContext context) { return values()[(Integer) nativeValue]; }
@Override public Object toNative() { return ordinal(); }
@Override public Class<?> nativeType() { return Integer.class; } }
复制代码


这个类实现了从 Integer 到 TestEnum 枚举的转换。


要想使用该 TestEnum 类的话,需要定义一个 interface:



public static interface EnumerationTestLibrary extends Library { TestEnum returnInt32Argument(TestEnum arg); }
复制代码


具体调用逻辑如下:


EnumerationTestLibrary lib = Native.load("testlib", EnumerationTestLibrary.class);assertEquals("Enumeration improperly converted", TestEnum.VALUE1, lib.returnInt32Argument(TestEnum.VALUE1));assertEquals("Enumeration improperly converted", TestEnum.VALUE2, lib.returnInt32Argument(TestEnum.VALUE2));
复制代码


可以看到,因为 NativeMapped 中已经包含了类型转换的信息,所以不需要再指定 TypeMapper 了。


注意,这里用到了 testlib,这个 testlib 是从 JNA 的 native 模块中编译出来的,如果你是 MAC 环境的话可以拷贝 JNA 代码,运行 ant native 即可得到,编译完成之后,将这个 libtestlib.dylib 拷贝到你项目中的 resources 目录下面 darwin-aarch64 或者 darwin-x86 即可。


有不会的同学,可以联系我。

总结

本文讲解了 JNA 中的类型映射规则和自定义类型映射的方法。


本文的代码:https://github.com/ddean2009/learn-java-base-9-to-20.git


本文已收录于 www.flydean.com

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

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

发布于: 2022 年 03 月 28 日阅读数: 35
用户头像

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

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

评论

发布
暂无评论
java高级用法之:在JNA中使用类型映射_Java_程序那些事_InfoQ写作平台