java⾼级⽤法之JNA中的Structure
⽬录
简介
native中的struct
Structure
特殊类型的Structure
结构体数组作为参数
结构体数组作为返回值
结构体中的结构体
结构体中的数组
结构体中的可变字段
结构体中的只读字段
总结
简介
前⾯我们讲到了JNA中JAVA代码和native代码的映射,虽然可以通过TypeMapper来将JAVA中的类型和native中的类型进⾏映射,但是native中的数据类型都是基础类型,如果native中的数据类型是复杂的struct类型该如何进⾏映射呢?
不⽤怕,JNA提供了Structure类,来帮助我们进⾏这些映射处理。
native中的struct
什么时候会⽤到struct呢?⼀般情况下,当我们需要⾃定义⼀个数据类的时候,⼀般情况下,在JAVA中需要定义⼀个class(在JDK17中,可以使⽤更加简单的record来进⾏替换),但是为⼀个数据结构定义class显然有些臃肿,所以在native语⾔中,有⼀些更简单的数据结构叫做struct。
我们先看⼀个struct的定义:
typedef struct _Point {
int x, y;
} Point;
上⾯的代码中,我们定义了⼀个Pointer的struct数据类下,在其中定义了int的x和y值表⽰Point的横纵坐标。
struct的使⽤有两种情况,⼀种是值传递,⼀种是引⽤传递。先来看下这两种情况在native⽅法中是怎么使⽤的:
引⽤传递:
Point* translate(Point* pt, int dx, int dy);
值传递:
Point translate(Point pt, int dx, int dy);
Structure
那么对于native⽅法中的struct数据类型的使⽤⽅式,应该如何进⾏映射呢? JNA为我们提供了Structur
e类。
默认情况下如果Structure是作为参数或者返回值,那么映射的是struct*,如果表⽰的是Structure中的⼀个字段,那么映射的是struct。
当然你也可以强制使⽤Structure.ByReference 或者 Structure.ByValue 来表⽰是传递引⽤还是传值。
我们看下上⾯的native的例⼦中,如果使⽤JNA的Structure来进⾏映射应该怎么实现:
指针映射:
class Point extends Structure { public int x, y; }
Point translate(Point pt, int x, int y);
...
Point pt = new Point();
Point result = translate(pt, 100, 100);
class Point extends Structure {
public static class ByValue extends Point implements Structure.ByValue { }
public int x, y;
}
Point.ByValue translate(Point.ByValue pt, int x, int y);
...
Point.ByValue pt = new Point.ByValue();
Point result = translate(pt, 100, 100);
Structure内部提供了两个interface,分别是ByValue和ByReference:
public abstract class Structure {
public interface ByValue { }
public interface ByReference { }
要使⽤的话,需要继承对应的interface。
特殊类型的Structure
除了上⾯我们提到的传值或者传引⽤的struct,还有其他更加复杂的struct⽤法。
结构体数组作为参数
⾸先来看⼀下结构体数组作为参数的情况:
void get_devices(struct Device[], int size);
对应结构体数组,可以直接使⽤JNA中对应的Structure数组来进⾏映射:
int size = ...
Device[] devices = new Device[size];
java jna<_devices(devices, devices.length);
结构体数组作为返回值
如果native⽅法返回的是⼀个指向结构体的指针,其本质上是⼀个结构体数组,我们应该怎么处理呢?
先看⼀下native⽅法的定义:
struct Display* get_displays(int* pcount);
void free_displays(struct Display* displays);
get_displays⽅法返回的是⼀个指向结构体数组的指针,pcount是结构体的个数。
对应的JAVA代码如下:
Display get_displays(IntByReference pcount);
void free_displays(Display[] displays);
对于第⼀个⽅法来说,我们只返回了⼀个Display,但是可以通过Array(int) ⽅法将其转换成为结构体数组。传⼊到第⼆个⽅法中,具体的调⽤⽅式如下:
IntByReference pcount = new IntByReference();
Display d = _displays(pcount);
Display[] displays = (Display[])d.Value());
...
lib.free_displays(displays);
结构体中的结构体
结构体中也可以嵌⼊结构体,先看下native⽅法的定义:
typedef struct _Point {
int x, y;
} Point;
typedef struct _Line {
Point end;
} Line;
对应的JAVA代码如下:
class Point extends Structure {
public int x, y;
}
class Line extends Structure {
public Point start;
public Point end;
}
如果是下⾯的结构体中的指向结构体的指针:
typedef struct _Line2 {
Point* p1;
Point* p2;
} Line2;
那么对应的代码如下:
class Point extends Structure {
public static class ByReference extends Point implements Structure.ByReference { }
public int x, y;
}
class Line2 extends Structure {
public Point.ByReference p1;
public Point.ByReference p2;
}
或者直接使⽤Pointer作为Structure的属性值:
class Line2 extends Structure {
public Pointer p1;
public Pointer p2;
}
Line2 line2;
Point p1, p2;
...
line2.p1 = p1.getPointer();
line2.p2 = p2.getPointer();
结构体中的数组
如果结构体中带有固定⼤⼩的数组:
typedef struct _Buffer {
char buf1[32];
char buf2[1024];
} Buffer;
那么我们在JAVA中需要指定数据的⼤⼩:
class Buffer extends Structure {
public byte[] buf1 = new byte[32];
public byte[] buf2 = new byte[1024];
}
如果结构体中是动态⼤⼩的数组:
typedef struct _Header {
int flags;
int buf_length;
char buffer[1];
} Header;
那么我们需要在JAVA的结构体中定义⼀个构造函数,传⼊bufferSize的⼤⼩,并分配对应的内存空间:结构体中的可变字段
默认情况下结构体中的内容和native memory的内容是⼀致的。JNA会在函数调⽤之前将Structure的内容写⼊到native memory 中,并且在函数调⽤之后,将 native memory中的内容回写到Structure中。
默认情况下是将结构体中的所有字段都进⾏写⼊和写出。但是在某些情况下,我们希望某些字段不进⾏⾃动更新。这个时候就可以使⽤volatile关键字,如下所⽰:
class Data extends com.sun.jna.Structure {
public volatile int refCount;
public int value;
}
...
Data data = new Data();
当然,你也可以强制使⽤Structure.writeField(String)来将字段信息写⼊内存中,或者使⽤ad() 来更新整个结构体的信息或者使⽤adField(“refCount”)来更新具体字段信息。
结构体中的只读字段
如果不想从JAVA代码中对Structure的内容进⾏修改,则可以将对应的字段标记为final。在这种情况下,虽然JAVA代码不能直接对其进⾏修改,但是仍然可以调⽤read⽅法从native memory中读取对应的内容并覆盖Structure中对应的值。
来看下JAVA中如何使⽤final字段:
class ReadOnly extends com.sun.jna.Structure {
public final int refCount;
{
// 初始化
refCount = -1;
// 从内存中读取数据
read();
}
}
注意所有的字段的初始化都应该在构造函数或者静态⽅法块中进⾏。
总结
结构体是native⽅法中经常会使⽤到的⼀种数据类型,JNA中对其进⾏映射的⽅法是我们要掌握的。
到此这篇关于java⾼级⽤法之JNA中的Structure的⽂章就介绍到这了,更多相关java 中的Structure内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。