简介JNA中的映射有很多种,库映射,函数映射,函数参数和返回值映射。libary和function之间的映射比较简单。之前我们用到的文章中已经说明了。对于类型映射,由于JAVA中的类型较多,这里把JNA的类型映射抽出来单独说明。类型映射的本质前面我们提到,JNA中有两种方法来映射JAVA中的方法和nativelibary中的方法,一种称为接口映射,另一种称为直接映射。但是我们是否考虑过这两个映射的性质?比如native有一个方法,我们如何将JAVA代码中的方法参数传递给native方法,并将native方法的返回值转换成JAVA中函数的返回类型呢?答案是序列化。因为基本上所有的交互都是二元交互。JAVA类型和原生类型之间的转换最简单的方法是JAVA类型和原生类型的底层数据长度保持一致,这样数据转换就更容易了。下面看一下JAVA类型和原生类型的映射和长度关系:CTypeNative类型的含义JavaTypechar8位整数bytewchar_t和平台相关的charshort16位整数shortint32位整数intintbooleanflagbooleanenum枚举类型int(通常)longlong,__int6464位整数longfloat32位浮点数floatdouble64位浮点数doublepointer(e.g.void*)platform-relatedBufferPointerpointer(e.g.void*),arrayplatform-related
[](primitivetypearray)the以上JAVA类型都是JDK自带的类型(Pointer除外)。除了JAVA自带的类型映射,JNA内部还定义了一些数据类型,可以和native类型进行映射:CTypeNativetype表示JavaTypelong和平台相关(32位或64位整数)NativeLongconstchar*string(nativeencodingorjna.encoding)Stringconstwchar_t*string(unicode)WStringchar**stringarrayString[]wchar_t**stringarray(unicode)WString[]void**pointersarrayPointer[]struct*structstructurepointerandstructureunionstructureUnionstruct[]structurearrayStructure[]void(*FP)()functionpointer(Javaornative)Callbackpointer(*)pointerPointerTypeotherintegertypeIntegerTypeother自定义映射类型NativeMappedTypeMapper除了定义的映射关系之外,您还可以使用TypeMapper自定义转换参数类型。首先我们看一下TypeMapper的定义:publicinterfaceTypeMapper{FromNativeConvertergetFromNativeConverter(Class>javaType);ToNativeConvertergetToNativeConverter(Class>javaType);}TypeMapper是一个接口,定义了两个转换器方法,即getFromNativeConverter和getToNativeConverter。如果你想使用TypeMapper,你需要实现它,这两个方法就足够了。我们看一下官方的W32APITypeMapper是怎么实现的:if(valueinstanceofString[]){returnnewStringArray((String[])value,true);}返回新的WString(value.toString());}@OverridepublicObjectfromNative(Objectvalue,FromNativeContextcontext){if(value==null)returnnull;返回值.toString();}@OverridepublicClass>nativeType(){returnWString.class;}};addTypeConverter(String.class,stringConverter);addToNativeConverter(String[].class,stringConverter);首先定义一个TypeConverter,在TypeConverter中实现toNative、fromNative、nativeType三个方法。本例中原生类型为WString,JAVA类型为String。而这个TypeConverter就是最后要用到的FromNativeConverter和ToNativeConverter。使用typeMapper,我应该如何使用它?最简单的方法是将它添加到Native.load的第三个参数,如下所示:TestLibrarylib=Native.load("testlib",TestLibrary.class,Collections.singletonMap(Library.OPTION_TYPE_MAPPER,mapper));调用Native.load方法时需要传入NativeMappedTypeMapper,提供JAVA类型和native类型的转换关系。TypeMapper可以看作是类型转换关系的外部维护者。可能很多朋友已经想到了。既然可以在JAVA类型之外维护转换关系,那么JAVA类型本身是否可以维护这种转换关系呢?答案是肯定的,我们只需要在需要实现转换类型关系的JAVA类型上实现NativeMapped接口即可。先看NativeMapped接口的定义:publicinterfaceNativeMapped{ObjectfromNative(ObjectnativeValue,FromNativeContextcontext);对象toNative();Class>nativeType();}可以看到NativeMapped中定义的方法基本和FromNativeConverter和ToNativeConverter中定义的方法是一样的。下面举一个具体的例子来说明NativeMapped应该如何使用。首先,我们定义一个枚举类来实现NativeMapped接口:publicenumTestEnumimplementsNativeMapped{VALUE1,VALUE2;@OverridepublicObjectfromNative(ObjectnativeValue,FromNativeContextcontext){returnvalues()[(Integer)nativeValue];}@OverridepublicObjecttoNative(){returnordinal();}@OverridepublicClass>nativeType(){returnInteger.class;}}这个类实现了从Integer到TestEnum枚举的转换。要使用TestEnum类,您需要定义一个接口:publicstaticinterfaceEnumerationTestLibraryextendsLibrary{TestEnumreturnInt32Argument(TestEnumarg);}具体调用逻辑如下:EnumerationTestLibrarylib=Native.load("testlib",EnumerationTestLibrary.class);assertEquals("枚举未正确转换",TestEnum.VALUE1,lib.returnInt32Argument(TestEnum.VALUE1));assertEquals("枚举转换不当",TestEnum.VALUE2,lib.returnInt32Argument(TestEnum.VALUE2));可以看出,因为在NativeMapped中已经包含了类型转换信息,所以不需要指定TypeMapper。请注意,这里使用了testlib。这个testlib是从JNA的原生模块编译而来的。如果是MAC环境,可以复制JNA代码,运行antnative即可获取。编译完成后,将这个libtestlib.dylib复制到你项目中的resources目录下,darwin-aarch64或者darwin-x86就可以了。不知道怎么做的同学可以联系我。小结本文讲解了JNA中的类型映射规则和自定义类型映射的方法。本文代码:https://github.com/ddean2009/learn-java-base-9-to-20.git本文已收录于www.flydean.com最通俗的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!