简介

JNA提供JAVA类型和native类型的映射关系,但是这一种映射关系只是一个大概的映射,我们在实际的应用中还有很多需要注意的事项,本文将会为大家详细讲解在使用类型映射中可能会出现的问题。一起来看看吧。

String

首先是String的映射,JAVA中的String实际上对应的是两种native类型:const char* 和 const wchar_t。默认情况下String会被转换成为char

char是ANSI类型的数据类型,而wchar_t是Unicode字符的数据类型,也叫做宽字符。

如果JAVA的unicode characters要转换成为char数组,那么需要进行一些编码操作,如果设置了jna.encoding,那么就会使用设置好的编码方式来进行编码。默认情况下编码方式是 "UTF8".

如果是WString,那么Unicode values可以直接拷贝到WString中,而不需要进行任何编码。

先看一个简单的例子:

 char* returnStringArgument(char *arg) {
return arg;
} wchar_t* returnWStringArgument(wchar_t *arg) {
return arg;
}

上面的native代码可以映射为:

String returnStringArgument(String s);
WString returnWStringArgument(WString s);

再来看一个不同的例子,假如native方法的定义是这样的:

int getString(char* buffer, int bufsize);

int getUnicodeString(wchar_t* buffer, int bufsize);

我们定义了两个方法,方法的参数分别是char* 和wchar_t*。

接下来看一下怎么在JAVA中定义方法的映射:

// Mapping A:
int getString(byte[] buf, int bufsize);
// Mapping B:
int getUnicodeString(char[] buf, int bufsize);

下面是具体的使用:

byte[] buf = new byte[256];
int len = getString(buf, buf.length);
String normalCString = Native.toString(buf);
String embeddedNULs = new String(buf, 0, len);

可能有同学会问了,既然JAVA中的String可以转换成为char*,为什么这里需要使用byte数组呢?

这是因为getString方法需要对传入的char数组中的内容进行修改,但是因为String是不可变的,所以这里是不能直接使用String的,我们需要使用byte数组。

接着我们使用Native.toString(byte[]) 将byte数组转换成为JAVA字符串。

再看一个返回值的情况:

// Example A: Returns a C string directly
const char* getString();
// Example B: Returns a wide character C string directly
const wchar_t* getString();

一般情况下,如果是native方法直接返回string,我们可以使用String进行映射:

// Mapping A
String getString();
// Mapping B
WString getString();

如果native code为String分配了内存空间,那么我们最好使用JNA中的Pointer作为返回值,这样我们可以在未来某些时候,释放所占用的空间,如下所示:

Pointer getString();

Buffers,Memory,数组和Pointer

什么时候需要用到Buffers和Memory呢?

一般情况下如果是基础数据的数组作为参数传到函数中的话,可以在JAVA中直接使用基础类的数组来替代。但是如果native方法在方法返回之后,还需要访问数组的话(保存了指向数组的指针),这种情况下使用基础类的数组就不太合适了,这种情况下,我们需要用到ByteBuffers或者Memory。

我们知道JAVA中的数组是带有长度的,但是对于native方法来说,返回的数组实际上是一个指向数组的指针,我们并不能知道返回数组的长度,所以如果native方法返回的是数组指针的话,JAVA代码中用数组来进行映射就是不合适的。这种情况下,需要用到Pointer.

Pointer表示的是一个指针,先看一下Pointer的例子,首先是native代码:

void* returnPointerArgument(void *arg) {
return arg;
} void* returnPointerArrayElement(void* args[], int which) {
return args[which];
}

接下来是JAVA的映射:

Pointer returnPointerArgument(Pointer p);

Pointer returnPointerArrayElement(Pointer[] args, int which);

除了基本的Pointer之外,你还可以自定义带类型的Pointer,也就是PointerType. 只需要继承PointerType即可,如下所示:

public static class TestPointerType extends PointerType {
public TestPointerType() { }
public TestPointerType(Pointer p) { super(p); }
}
TestPointerType returnPointerArrayElement(TestPointerType[] args, int which);

再看一下字符串数组:

char* returnStringArrayElement(char* args[], int which) {
return args[which];
} wchar_t* returnWideStringArrayElement(wchar_t* args[], int which) {
return args[which];
}

对应的JAVA映射如下:

String returnStringArrayElement(String[] args, int which);

WString returnWideStringArrayElement(WString[] args, int which);

对应Buffer来说,JAVA NIO中提供了很多类型的buffer,比如ByteBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer和DoubleBuffer等。这里以ByteBuffer为例,来看一下具体的使用.

首先看下native代码:

int32_t fillInt8Buffer(int8_t *buf, int len, char value) {
int i; for (i=0;i < len;i++) {
buf[i] = value;
}
return len;
}

这里将buff进行填充,很明显后续还需要使用到这个buffer,所以这里使用数组是不合适的,我们可以选择使用ByteBuffer:

int fillInt8Buffer(ByteBuffer buf, int len, byte value);

然后看下具体怎么使用:

TestLibrary lib = Native.load("testlib", TestLibrary.class);

        ByteBuffer buf  = ByteBuffer.allocate(1024).order(ByteOrder.nativeOrder());
final byte MAGIC = (byte)0xED;
lib.fillInt8Buffer(buf, 1024, MAGIC);
for (int i=0;i < buf.capacity();i++) {
assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
}

可变参数

对于native和JAVA本身来说,都是支持可变参数的,我们举个例子,在native方法中:

int32_t addVarArgs(const char *fmt, ...) {
va_list ap;
int32_t sum = 0;
va_start(ap, fmt); while (*fmt) {
switch (*fmt++) {
case 'd':
sum += va_arg(ap, int32_t);
break;
case 'l':
sum += (int) va_arg(ap, int64_t);
break;
case 's': // short (promoted to 'int' when passed through '...')
case 'c': // byte/char (promoted to 'int' when passed through '...')
sum += (int) va_arg(ap, int);
break;
case 'f': // float (promoted to ‘double’ when passed through ‘...’)
case 'g': // double
sum += (int) va_arg(ap, double);
break;
default:
break;
}
}
va_end(ap);
return sum;
}

对应的JAVA方法映射如下:

public int addVarArgs(String fmt, Number... args);

相应的调用代码如下:

int arg1 = 1;
int arg2 = 2;
assertEquals("32-bit integer varargs not added correctly", arg1 + arg2,
lib.addVarArgs("dd", arg1, arg2));

总结

本文介绍了在使用JNA方法映射中应该注意的一些细节和具体的使用问题。

本文的代码:https://github.com/ddean2009/learn-java-base-9-to-20.git

本文已收录于 http://www.flydean.com/05-jna-type-mapping-details-md/

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

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

java高级用法之:JNA类型映射应该注意的问题的更多相关文章

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

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

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

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

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

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

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

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

  5. java高级用法之:在JNA中使用类型映射

    目录 简介 类型映射的本质 TypeMapper NativeMapped 总结 简介 JNA中有很多种映射,library的映射,函数的映射还有函数参数和返回值的映射,libary和函数的映射比较简 ...

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

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

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

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

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

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

  9. java 高级用法整理

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

随机推荐

  1. [SPDK/NVMe存储技术分析]014 - (NVMe over PCIe)Host端的命令处理流程

    NVMe over PCIe最新的NVMe协议是1.3. 在7.2.1讲了Command Processing流程.有图有真相. This section describes command subm ...

  2. 安装配置Snort和barnyard2

    1.安装依赖包 yum install –y gcc flex bison zlib* libpcap* tcpdump gcc-c++ zlib* libdnet libdnet-devel pcr ...

  3. IIS将应用程序池配置为在计划时间执行回收 (IIS 7)

    将应用程序池配置为在计划时间执行回收 您可以通过以下方法执行此过程:使用用户界面 (UI).在命令行窗口中运行 Appcmd.exe 命令.直接编辑配置文件或编写 WMI 脚本. 如下只介绍用户界面U ...

  4. Java9的模块化是什么

    Java9新特性中的模块化到底是什么 Java9中的一个重大特性是增加了一种新型的程序设计组件 - 模块. 官方对模块的定义为:一个被命名的,代码和数据的自描述集合.( the module, whi ...

  5. Windows10与Centos7双系统安装踩的坑

    1. 首先安装windows(太简单不说了) 2.然后安装Centos7(太简单不说了) 3.注意:安装完Centos7重启电脑进入系统引导项突然发现没有Windows引导项 0x06 恢复Windo ...

  6. sqlserver下载地址及密匙

    SqlServer 2017 下载地址及密钥 下载地址: ed2k://|file|cn_sql_server_2017_developer_x64_dvd_11296175.iso|17697771 ...

  7. MariaDB数据库设置用户密码

    SET PASSWORD [FOR user] = { PASSWORD('some password') | OLD_PASSWORD('some password') | 'encrypted p ...

  8. We're sorry but demo3 doesn't work properly without JavaScript enabled. Please enable it to continue.

    今天遇到一个问题为 vue请求得到的响应为 We're sorry but demo3 doesn't work properly without JavaScript enabled. Please ...

  9. 什么是切点JoinPoint?

    程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理. 在 Spring AOP 中, join point 总是方法的执行点.

  10. gateway聚合swagger3统一管理api文档

    springboot微服务整合swagger3方法很简单,下文会演示.但是在分布式项目中如果每个微服务都需要单独的分开访问获取接口文档就不方便了,本文将详细讲解springcloud gateway网 ...