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类型的映射和长度关系:
| 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中使用类型映射的更多相关文章
- java高级用法之:在JNA中将本地方法映射到JAVA代码中
目录 简介 Library Mapping Function Mapping Invocation Mapping 防止VM崩溃 性能考虑 总结 简介 不管是JNI还是JNA,最终调用的都是nativ ...
- java高级用法之:JNA中的Structure
目录 简介 native中的struct Structure 特殊类型的Structure 结构体数组作为参数 结构体数组作为返回值 结构体中的结构体 结构体中的数组 结构体中的可变字段 结构体中的只 ...
- java高级用法之:JNA中的Memory和Pointer
目录 简介 Pointer 特殊的Pointer:Opaque Memory 总结 简介 我们知道在native的代码中有很多指针,这些指针在JNA中被映射成为Pointer.除了Pointer之外, ...
- java高级用法之:JNA中的Function
目录 简介 function的定义 Function的实际应用 总结 简介 在JNA中,为了和native的function进行映射,我们可以有两种mapping方式,第一种是interface ma ...
- java高级用法之:JNA中的回调
目录 简介 JNA中的Callback callback的应用 callback的定义 callback的获取和应用 在多线程环境中使用callback 总结 简介 什么是callback呢?简单点说 ...
- java高级用法之:调用本地方法的利器JNA
目录 简介 JNA初探 JNA加载native lib的流程 本地方法中的结构体参数 总结 简介 JAVA是可以调用本地方法的,官方提供的调用方式叫做JNI,全称叫做java native inter ...
- java高级用法之:JNA类型映射应该注意的问题
目录 简介 String Buffers,Memory,数组和Pointer 可变参数 总结 简介 JNA提供JAVA类型和native类型的映射关系,但是这一种映射关系只是一个大概的映射,我们在实际 ...
- java 高级用法整理
一.retentionpolicy.class vs runtime区别 java5,增加了注解的功能:其中retentionpolicy注解的生命周期,提供了三种选择策略 source.class和 ...
- java高级用法之:无所不能的java,本地方法调用实况
目录 简介 JDK的本地方法 自定义native方法 总结 简介 相信每个程序员都有一个成为C++大师的梦想,毕竟C++程序员处于程序员鄙视链的顶端,他可以俯视任何其他语言的程序员. 但事实情况是,无 ...
随机推荐
- Spring——自动装配的三种实现方式
依赖注入的本质是装配,装配是依赖注入的具体行为 spring会在上下文中自动寻找,并自动给bean装配属性 自动装配的三种方式 (1).在xml中显式的装配 (2).在java中显式的装配 (3).隐 ...
- How to Rebase Git Branch
转自:How to Rebase Git Branch (with 3 steps) | Git Rebase | W3Docs Steps to rebasing branch Fetching c ...
- Centos 7.5 通过yum安装GNOME Desktop时出现:file /boot/efi/EFI/centos from install of fwupdate-efi-12-5.el7.centos.x86_64 conflicts with file from package grub2-common-1:2.02-0.65.el7.centos.2.noarch
系统版本为: [root@s10 ~]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) 由于管理kvm虚拟机的需求,需要安装 ...
- _u32定义
驱动开发的原则: 能用__u32就最好用它,或者用u_int32_t之类的也可以,但不要直接用unsigned int等默认的数据类型.目的是让别人明白,你这个变量占多大内存. 原因: 1.你不能确定 ...
- 学习SpringMVC必知必会(7)~springmvc的数据校验、表单标签、文件上传和下载
输入校验是 Web 开发任务之一,在 SpringMVC 中有两种方式可以实现,分别是使用 Spring 自带的验证 框架和使用 JSR 303 实现, 也称之为 spring-validator 和 ...
- Mycat 数据切分 看这一篇就够了
数据切分 数据切分指的是通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库上面,以达到分散单台设备负载的效果. 数据的切分根据其切分规则的类型,可以分为两种切分模式.一种 ...
- mock axios vue的数据传递关系
最近有一个项目要快速出一个前端demo,选择了vue-element框架(虽然其实很想用react,但是为了求快) 本人的基础只有初级的html/css/js知识,和python后端的一些简单工作,和 ...
- Apache Tomcat如何高并发处理请求
介绍 作为常用的http协议服务器,tomcat应用非常广泛.tomcat也是遵循Servelt协议的,Servelt协议可以让服务器与真实服务逻辑代码进行解耦.各自只需要关注Servlet协议即可. ...
- IDEA 常用快捷键操作
自定义设置及查询: 操作路径:file-setting-Keymap-Editor actions 右击需要修改的action操作,或者右击Editor actions,选择添加Add Keyboar ...
- java中的异常体系?throw和throws的区别?
一.java中的异常体系 Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常.其中异常类Exception又分为运行时异常(Ru ...