java高级用法之JNA类型映射应该注意的问题
简介
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/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
LEDSMART隐私保护指引引言本应用(以下简称LEDSMART或谭桂兴)尊重并保护所有使用服务用户的个人隐私权。我们非常重视您的个人信息,并让您对个人信息拥有控制权,我们会使用强大的加密技术来保护您的隐私,
燃油车和新能源汽车如何选择?近年来随着互联网和造车新势力的日益兴起及新能源汽车产业的突飞猛进,如特斯拉蔚来小鹏理想等为代表的新能源汽车给传统车带来了一定的冲击,也给消费者在购车的时候有了更多的选择,在面对新能
CCV创始合伙人周炜我先是喜马拉雅的用户,后来才是喜马拉雅的投资人周炜连年名列福布斯中国最佳创投人TOP30,是喜马拉雅的第一位投资人创世伙伴资本CCV创始合伙人,从事VC投资十多年。高达30的独角兽命中率让周炜享誉创投圈,喜马拉雅京东商城京东数
网友数博会数字生活数不尽的便捷来源原创稿作为全国首个大数据综合试验区全国第一个提出大数据发展战略行动的省份,贵州化茧成蝶,以大数据为引领重构后发地区整体发展方式,从一张白纸蜕变成大数据高地,成为大数据国家战略中
小康股份年报研发投入超19亿元,营收占比11来源环球网4月29日晚间,小康股份(601127。SH)发布2021年度业绩报告。公告显示,2021年小康股份全年实现营业收入167。18亿元,同比增长16。89,年度研发投入达1
flex不香吗,你还在傻傻地用float吗我是小渔歌,点击右上方关注,每天为你分享前端技术知识干货。大家在做移动端的时候,还有人在用浮动(float)布局吗?弹性盒模型(flex)可以解决一切,当你用熟悉了之后,你绝对不会
12512G不到2499元!MotoedgeS30又降价买到不就是赚到?相信大家也看到了,现阶段不少国内手机厂商推出旗舰的价格是越来越往苹果靠齐了,各个都想冲刺高端,然而想要冲刺高端,可不仅仅是对标苹果价格那么简单的。据悉,2021年国内5000元以上
欧盟给谷歌们再上紧箍咒来源人民网据报道,4月23日,欧盟成员国与欧洲议会的谈判代表就欧盟数字服务法达成一致。该法案要求互联网平台企业采取更多措施删除非法和有害的在线内容,否则可能面临巨额罚款。外媒普遍认
宇宙中有多少颗恒星?比地球上的沙粒还要多得多宇宙有多大?没有边界!目前人类能观测到的范围,为930亿光年,这是一个可怕的数字。我们都知道,光速是目前已知的宇宙中最快的物质,每秒30万千米,一秒钟就能围绕地球转七圈半。但是,光
从狂妄不羁的马斯克成就非凡,看科技创新生态的重要性近日,马斯克与推特公司达成440亿美元的收购意向。马斯克曾经的又一个狂言变为了现实。更诡吊的是,马斯克又在推特发文,宣布要收购可口可乐接下来我要收购可口可乐公司,再把可卡因重新加进
字节再出王炸消息增资40亿字节潜伏7年,只为一件事布局金融版图。记者洛重阳编辑呦呦两年增资3次,从10亿到90亿元,字节跳动发展网贷业务的野心和决心在不断增长。4月27日,深圳市中融小额贷款有限公司(下称深