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++程序员处于程序员鄙视链的顶端,他可以俯视任何其他语言的程序员. 但事实情况是,无 ...
随机推荐
- _u32定义
驱动开发的原则: 能用__u32就最好用它,或者用u_int32_t之类的也可以,但不要直接用unsigned int等默认的数据类型.目的是让别人明白,你这个变量占多大内存. 原因: 1.你不能确定 ...
- 一键生成mapper、mapperxml等文件——MybatisX插件的使用
本文首发于西二blogs:一键生成mapper.mapperxml等文件--MybatisX插件的使用 搬运请务必转载出处. MybatisX插件使用--为快速开发而生 前言:其实很久以前我就非常厌恶 ...
- python写一个数字字典生成器
#数字字典生成器 by qianxiao996 #博客地址:https://blog.csdn.net/qq_36374896 #此程序输入开始结束和位数即可在程序所在目录下生成字典 #只支持数字生成 ...
- Skye无人机刷Betaflight详细图文教程
前言 首先十分感谢B站TASKBL up主的视频教程以及他的耐心指导,视频链接Skye 原机主板刷BetaFlight 参考教程_哔哩哔哩_bilibili.整个改造过程耗时三天,现把改造过程以及遇 ...
- TTL、RS232、RS485、UART、串口的关系和常见半双工、全双工协议
串口(UART口).COM口.USB口.DB9.DB25是指的物理接口形式(硬件) TTL.RS-232.RS-485是指的电平标准(电平信号) 我们单片机嵌入式常用的串口有三种(TTL/RS-2 ...
- Java使用多线程异步执行批量更新操作
import com.google.common.collect.Lists; import org.apache.commons.collections.CollectionUtils; impor ...
- 面试问题之操作系统:linux线程API
https://blog.csdn.net/youwotianya/article/details/80933449
- springboot项目如何添加热部署
环境jdk1.8.maven3.6.使用工具为idea 1.在pom.xml文件中添加依赖 <dependency> <groupId>org.springframework. ...
- Qunee for HTML5 v1.6新版本发布
Qunee for HTML5 V1.6正式发布,修复了一些 BUG,增加了滚动条支持,改进了编辑器,增加了JSON 导入导出.告警冒泡.连线流动,UI 定制等扩展示例,欢迎 访问 导航面板 增加了滚 ...
- 前端基础问题整理-HTML相关
DOCTYPE的作用以及常见的DOCTYPE类型 <!DOCTYPE>声明位于文档中的最前面的位置,处于 <html> 标签之前,用来告知浏览器页面目前的文件是用哪种版本的HT ...