您的位置:首页 > 编程语言 > Java开发

JNA 基础篇<一> 初识JNA

2016-05-09 00:00 471 查看
摘要: 由于项目需要,最近使用了JNA调用原生函数,过程中遇到不少问题,花费了不少时间,因此写出来和大家分享下.
本小结主要介绍什么是JNA,JNA如何调用原生函数,调用原生函数的原理,以及java和原生代码的数据类型映射

1.JNA简介

JNA(Java Native Access)框架是一个开源的Java 框架,是SUN 公司主导开发的,建立在经典的JNI 的基础之上的一个框架。JNA 项目地址:https://jna.dev.java.net/。

JNI 是Java 调用原生函数唯一的机制。JNA 也是建立在JNI 技术之上的,它简化了Java调用原生函数的过程。使用JNA可以使你像调用java方法一样直接调用本地方法,极大地扩展了java平台的整合能力。

JNA 提供了一个动态的C 语言编写的转发器,可以自动实现Java 和C 的数据类型映射。

2.JNA调用原生函数示例

假设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中声明原型。

对于加载动态链接库,如果使用JNI,需要使用System. loadLibrary 方法,把专为JNI 编写的动态链接库载入进来。这个动态链接库实际上是我们真正需要的动态链接库的代理。若使用JNA,不需要编写作为代理的动态链接库,使用JNA 类库的Native 类的loadLibrary 方法直接把我们需要的动态链接库载入进来。

上面代码中,使用了java单例模式,接口的静态变量返回的是接口的唯一实例,该实例由JNA通过反射动态创建,通过这个对象,可以调用动态链接库发布的所有函数。

4.java和原生代码的类型映射

JNA 使用的数据类型是Java 的数据类型。而原生函数中使用的数据类型是原生函数的编程语言使用的数据类型。可能是C,Delphi,汇编等语言的数据类型。如数据类型映射不一致,在调用中可能会发生无法预知的行为,可能是调用不成功,也可能不能正确解析返回数据。

C,java和操作系统数据类型对应表

native typesizejava typecommon windows types
char8-bit integer
byteBYTE, TCHAR
short16-bit integershortWORD
wchar_t16/32-bit charactercharTCHAR
int32-bit integerintDWORD
intboolean valuebooleanBOOL
long32/64-bit integerNativeLongLONG
long long64-bit integerlong__int64
float32-bit FPfloat
double64-bit FPdouble
char*C stringStringLPTCSTR
void*pointerPointerLPVOID, HANDLE, LPXXX
pointerBuffer/Pointer平台依赖(32 或64 位指针)
pointer/array<T>[] (基本类型的数组)32 或64 位指针(参数/返回值)
邻接内存(结构体成员)
wchar_t*WString\0 结束的数组(unicode)
char**String[]\0 结束的数组的数组
wchar_t**WString[]\0 结束的宽字符数组的数组
struct*/structStructure指向结构体的指针(参数或返回值) (或者明确指定是结构体指针)/结构体(结构体的成员) (或者明确指定是结构体)
unionUnion等同于结构体
Structure[]struct[]结构体的数组,邻接内存
<T> (*fp)()CallbackJava 函数指针或原生函数指针
variesNativeMapped依赖于定义
pointerPointerType和Pointer 相同
5.跨平台、跨语言调用原则:
尽量使用基本、简单的数据类型;
尽量少跨平台、跨语言传递数据!
如果有复杂的数据类型需要在Java 和原生函数中传递,那么我们就必须在Java 中模拟
大量复杂的原生类型。这将大大增加实现的难度,甚至无法实现。
如果在Java 和原生函数间存在大量的数据传递,那么一方面,性能会有很大的损失。
更为重要的是,Java 调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这
些Java 数据。这些数据,JVM 的GC 不能管理,会造成内存碎片。
如果在你需要调用的动态链接库中,有复杂的数据类型和庞大的跨平台数据传递。那么
你应该另外写一些原生函数,把需要传递的数据类型简化,把需要传递的数据量简化。

本小节到此结束,下一节将重点介绍结构体

欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/672283
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息