简介

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 平台相关

[] (原始类型数组)

上面的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 ( *) 指针 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

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

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

java高级用法之:在JNA中使用类型映射的更多相关文章

  1. java高级用法之:在JNA中将本地方法映射到JAVA代码中

    目录 简介 Library Mapping Function Mapping Invocation Mapping 防止VM崩溃 性能考虑 总结 简介 不管是JNI还是JNA,最终调用的都是nativ ...

  2. java高级用法之:JNA中的Structure

    目录 简介 native中的struct Structure 特殊类型的Structure 结构体数组作为参数 结构体数组作为返回值 结构体中的结构体 结构体中的数组 结构体中的可变字段 结构体中的只 ...

  3. java高级用法之:JNA中的Memory和Pointer

    目录 简介 Pointer 特殊的Pointer:Opaque Memory 总结 简介 我们知道在native的代码中有很多指针,这些指针在JNA中被映射成为Pointer.除了Pointer之外, ...

  4. java高级用法之:JNA中的Function

    目录 简介 function的定义 Function的实际应用 总结 简介 在JNA中,为了和native的function进行映射,我们可以有两种mapping方式,第一种是interface ma ...

  5. java高级用法之:JNA中的回调

    目录 简介 JNA中的Callback callback的应用 callback的定义 callback的获取和应用 在多线程环境中使用callback 总结 简介 什么是callback呢?简单点说 ...

  6. java高级用法之:调用本地方法的利器JNA

    目录 简介 JNA初探 JNA加载native lib的流程 本地方法中的结构体参数 总结 简介 JAVA是可以调用本地方法的,官方提供的调用方式叫做JNI,全称叫做java native inter ...

  7. java高级用法之:JNA类型映射应该注意的问题

    目录 简介 String Buffers,Memory,数组和Pointer 可变参数 总结 简介 JNA提供JAVA类型和native类型的映射关系,但是这一种映射关系只是一个大概的映射,我们在实际 ...

  8. java 高级用法整理

    一.retentionpolicy.class vs runtime区别 java5,增加了注解的功能:其中retentionpolicy注解的生命周期,提供了三种选择策略 source.class和 ...

  9. java高级用法之:无所不能的java,本地方法调用实况

    目录 简介 JDK的本地方法 自定义native方法 总结 简介 相信每个程序员都有一个成为C++大师的梦想,毕竟C++程序员处于程序员鄙视链的顶端,他可以俯视任何其他语言的程序员. 但事实情况是,无 ...

随机推荐

  1. python3判断一个数是否为素数

    while True: num = int(input('请输入一个数:')) for i in range(2,num):#判断在num之前的数能不能把num整除 if(num%i == 0): p ...

  2. Vmware安装Ubuntu16.4的过程及出现问题的解决

    镜像下载.域名解析.时间同步请点击 阿里云开源镜像站 1.下载Ubuntu镜像文件 Ubuntu16.4镜像文件下载地址:https://mirrors.aliyun.com/ubuntu-relea ...

  3. Centos7下开启防火墙,允许通过的端口

    1.查看防火墙状态 systemctl status firewalld 2.如果不是显示active状态,需要打开防火墙 systemctl start firewalld 3.# 查看所有已开放的 ...

  4. unittest+HtmlTestRunner+python接口自动化测试:用例失败发送邮件

    一点啰嗦:发送邮件python中有另一个支持的第三方库yagmail更轻量级,代码参考可移步至此:https://www.cnblogs.com/princessironfan/p/13220601. ...

  5. AVRmega16 LED 例程

                                                                                      AVRmega16 LED 例程   ...

  6. Python库国内镜像

    中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/ http://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣 http://py ...

  7. 12.11 Exception

    12.11exception 1.类别 检查性异常:用户错误或者问题引起的异常,此类异常程序员无法预见.例如:打开一个不存在的文件,用户输入错误(在只能输数字的地方输入汉字)...... 运行时异常: ...

  8. Android Studio Gradle project sync failed

    使用Android Studio 1.1.0创建新项目后,运行报以下错: Error:Unable to start the daemon process. This problem might be ...

  9. fiber核心(react 16)?

    旧: 浏览器渲染引擎单线程, 计算DOM树时锁住整个线程, 所有行为同步发生, 有效率问题, 期间react会一直占用浏览器主线程,如果组件层级比较深,相应的堆栈也会很深,长时间占用浏览器主线程, 任 ...

  10. 如何用 Java 代码列出一个目录下所有的文件?

    如果只要求列出当前文件夹下的文件,代码如下所示: import java.io.File; class Test12 { public static void main(String[] args) ...