1.JNA简介java
JNA(Java Native Access)框架是一个开源的Java 框架,是SUN 公司主导开发的,创建在经典的JNI 的基础之上的一个框架。JNA 项目地址:https://jna.dev.java.net/。编程
JNI 是Java 调用原生函数惟一的机制。JNA 也是创建在JNI 技术之上的,它简化了Java调用原生函数的过程。使用JNA可使你像调用java方法同样直接调用本地方法,极大地扩展了java平台的整合能力。windows
JNA 提供了一个动态的C 语言编写的转发器,能够自动实现Java 和C 的数据类型映射。数组
2.JNA调用原生函数示例app
假设libCms.dll动态连接库中发布了以下C函数:框架
LONG startListen(CmsListenParam listenPara);
该函数做用是根据监听参数启动一个监听,CmsListenParam具体是什么暂时不用管,后面会详细介绍。为了调用这个原生函数,使用JNA,编写以下java代码:编程语言
public interface CmsServer extends StdCallLibrary{ //根据dll名字加载库文件 CmsServer instance=(CmsServer) Native.loadLibrary("libCms",CmsServer.class); NativeLong startListen(CmsListenParam listenPara); }
而后咱们就能够像调用java代码同样调用原生函数了:函数
public static void main(String[] args) { CmsServer.instance.startListen(new CmsListenParam()); System.out.println("调用成功"); }
3.JNA调用原生函数的模式性能
JNI中使用Native关键字来声明一个java方法表明外部的原生函数,JNA不使用Native表明原生函数,而是使用java interface来表明动态连接库中表明的全部原生函数,对于不使用的原生函数,能够不在interface中声明原型。 spa
对于加载动态连接库,若是使用JNI,须要使用System. loadLibrary 方法,把专为JNI 编写的动态连接库载入进来。这个动态连接库其实是咱们真正须要的动态连接库的代理。若使用JNA,不须要编写做为代理的动态连接库,使用JNA 类库的Native 类的loadLibrary 方法直接把咱们须要的动态连接库载入进来。
上面代码中,使用了java单例模式,接口的静态变量返回的是接口的惟一实例,该实例由JNA经过反射动态建立,经过这个对象,能够调用动态连接库发布的全部函数。
4.java和原生代码的类型映射
JNA 使用的数据类型是Java 的数据类型。而原生函数中使用的数据类型是原生函数的编程语言使用的数据类型。多是C,Delphi,汇编等语言的数据类型。如数据类型映射不一致,在调用中可能会发生没法预知的行为,多是调用不成功,也可能不能正确解析返回数据。
C,java和操做系统数据类型对应表
native type | size | java type | common windows types |
char | 8-bit integer |
byte | BYTE, TCHAR |
short | 16-bit integer | short | WORD |
wchar_t | 16/32-bit character | char | TCHAR |
int | 32-bit integer | int | DWORD |
int | boolean value | boolean | BOOL |
long | 32/64-bit integer | NativeLong | LONG |
long long | 64-bit integer | long | __int64 |
float | 32-bit FP | float | |
double | 64-bit FP | double | |
char* | C string | String | LPTCSTR |
void* | pointer | Pointer | LPVOID, HANDLE, LPXXX |
pointer | Buffer/Pointer | 平台依赖(32 或64 位指针) | |
pointer/array | <T>[] (基本类型的数组) | 32 或64 位指针(参数/返回值) 邻接内存(结构体成员) |
|
wchar_t* | WString | \0 结束的数组(unicode) | |
char** | String[] | \0 结束的数组的数组 | |
wchar_t** | WString[] | \0 结束的宽字符数组的数组 | |
struct*/struct | Structure | 指向结构体的指针(参数或返回值) (或者明确指定是结构体指针)/结构体(结构体的成员) (或者明确指定是结构体) | |
union | Union | 等同于结构体 | |
Structure[] | struct[] | 结构体的数组,邻接内存 | |
<T> (*fp)() | Callback | Java 函数指针或原生函数指针 | |
varies | NativeMapped | 依赖于定义 | |
pointer | PointerType | 和Pointer 相同 |
5.跨平台、跨语言调用原则:
尽可能使用基本、简单的数据类型;
尽可能少跨平台、跨语言传递数据!
若是有复杂的数据类型须要在Java 和原生函数中传递,那么咱们就必须在Java 中模拟
大量复杂的原生类型。这将大大增长实现的难度,甚至没法实现。
若是在Java 和原生函数间存在大量的数据传递,那么一方面,性能会有很大的损失。
更为重要的是,Java 调用原生函数时,会把数据固定在内存中,这样原生函数才能够访问这
些Java 数据。这些数据,JVM 的GC 不能管理,会形成内存碎片。
若是在你须要调用的动态连接库中,有复杂的数据类型和庞大的跨平台数据传递。那么
你应该另外写一些原生函数,把须要传递的数据类型简化,把须要传递的数据量简化。
本小节到此结束,下一节将重点介绍结构体
欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/672283