JNA 基础篇<一> 初识JNA

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
char

 8-bit integer

byteBYTE, TCHAR
short16-bit integershortWORD
wchar_t16/32-bit characterchar

TCHAR

int32-bit integerint

DWORD

intboolean valuebooleanBOOL
long32/64-bit integerNativeLongLONG
long long64-bit integerlong

__int64

float32-bit FPfloat 
double64-bit FPdouble 
char*C stringStringLPTCSTR
void*pointerPointerLPVOID, 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)() CallbackJava 函数指针或原生函数指针
varies NativeMapped依赖于定义
pointer PointerType和Pointer 相同


5.跨平台、跨语言调用原则:
尽量使用基本、简单的数据类型;
尽量少跨平台、跨语言传递数据!
如果有复杂的数据类型需要在Java 和原生函数中传递,那么我们就必须在Java 中模拟
大量复杂的原生类型。这将大大增加实现的难度,甚至无法实现。
如果在Java 和原生函数间存在大量的数据传递,那么一方面,性能会有很大的损失。
更为重要的是,Java 调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这
些Java 数据。这些数据,JVM 的GC 不能管理,会造成内存碎片。
如果在你需要调用的动态链接库中,有复杂的数据类型和庞大的跨平台数据传递。那么
你应该另外写一些原生函数,把需要传递的数据类型简化,把需要传递的数据量简化。

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

欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/672283

转载于:https://my.oschina.net/7001/blog/672283

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值