1.JNA简介java
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 的数据类型映射。windows
2.JNA调用原生函数示例数组
假设libCms.dll动态连接库中发布了以下C函数:app
lc_init(string filename,Param param)
该函数做用是根据监听参数启动一个监听,CmsListenParam具体是什么暂时不用管,后面会详细介绍。为了调用这个原生函数,使用JNA,编写以下java代码:框架
private interface GetBroadcastIds extends Library { GetBroadcastIds INSTANCE = (GetBroadcastIds) Native.loadLibrary("LCAudioThrDll",GetBroadcastIds.class); int lc_init(String filename,Param param); }
而后咱们就能够像调用java代码同样调用原生函数了:编程语言
public static void main(String[] args) { String filename = "D:\\KuGou\\体面.mp3"; Param param = new Param(); param.sethWnd(0L);//Windows窗口句柄,若是不为NULL,线程将事件消息发送到此窗口 param.setPriority(0);//音频流优先级0~255 param.setCastMode(2);//播放方式,单播,组播和广播[0,1,2] param.setVolume(80);//音量0~100 param.setIP(3232261139L);//IP转数字 param.setTone(0);//音调(未使用)0~15 param.setBass(200);//低音频率0~3000 param.setTreble(3000);//高音频率200~20000 param.setTrebleEn(0);//高音放大因子0~255 param.setBassEn(0);//低音放大因子0~255 param.setSourcType(0);//音频数据源,0表示数据源为文件,1表示数据源为声卡输入[0,1,2,3] DDMineService.instance.lc_init(filename,param); System.out.println("调用成功"); }
3.JNA调用原生函数的模式函数
JNA中使用Native关键字来声明一个Java方法表明外部的原生函数,JNA不使用Native表明原生函数,而是使用java interface来表明动态连接库中表明的全部原生函数,对于不使用的原生函数,能够不在interface中声明原型。 性能
4.java和原生代码的类型映射
JNA 使用的数据类型是Java 的数据类型。而原生函数中使用的数据类型是原生函数的编程语言使用的数据类型。多是C,Delphi,汇编等语言的数据类型。如数据类型映射不一致,在调用中可能会发生没法预知的行为,多是调用不成功,也可能不能正确解析返回数据。
C#,java和操做系统数据类型对应表
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 不能管理,会形成内存碎片。
若是在你须要调用的动态连接库中,有复杂的数据类型和庞大的跨平台数据传递。那么
你应该另外写一些原生函数,把须要传递的数据类型简化,把须要传递的数据量简化。