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

8次阅读

共计 4069 个字符,预计需要花费 11 分钟才能阅读完成。

简介

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

类型映射的实质

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

然而咱们有没有思考过这两种映射的实质是什么呢?

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

答案就是序列化。

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

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

C Type Native 类型的含意 Java Type
char 8-bit 整型 byte
wchar_t 和平台相干 char
short 16-bit 整型 short
int 32-bit 整型 int
int boolean flag boolean
enum 枚举类型 int (usually)
long long, __int64 64-bit 整型 long
float 32-bit 浮点数 float
double 64-bit 浮点数 double
pointer (e.g. void*) 平台相干 Buffer Pointer
pointer (e.g. void*), array 平台相干 <P>[] ( 原始类型数组)

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

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

C Type Native 类型的含意 Java Type
long 和平台相干 (32- or 64-bit integer) NativeLong
const char* 字符串 (native encoding or jna.encoding) String
const wchar_t* 字符串 (unicode) WString
char** 字符串数组 String[]
wchar_t** 字符串数组 (unicode) WString[]
void** pointers 数组 Pointer[]
struct* struct 构造体指针和构造体 Structure
union 构造体 Union
struct[] 构造体数组 Structure[]
void (*FP)() 函数指针 (Java or native) Callback
pointer (<T> *) 指针 PointerType
other 整数类型 IntegerType
other 自定义映射类型 NativeMapped

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

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

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

正文完
 0