JNA交流
JNA交流 1、初识JNA
1.1、什么是JNA 1.2、JNA和JNI的关系 1.3、JNA相较于JNI的优缺点
3、JNA的用法 4、JNA在TIMS系统中的应用
1、初识JNA 1.1、什么是JNA
JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个 框架。JNA提供一组Java工具类用于在运行期间动态访问系统本地库(native library:如Window的dll和Linux的 so)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构, JNA将自动实现Java接口到native function的映射。
JNA类库使用一个很小的本地类库sub 动态的调用本地代码。程序员只需要使用一个特定的java接口描述一下将要 调用的本地代码的方法的结构和一些基本属性。这样就省了为了适配多个平台而大量的配置和编译代码。因为调用 的都是JNA提供的公用jar 包中的接口。
JNA是为了程序员调用本地代码更方便,跑的更快以及减少出错。 1.2、JNA和JNI的关系
JNA是基于JNI进行开发的Java框架,JNA类似于.NET的P/Invoke,使用JNI调用dll/so共享类库是非常麻烦和痛苦 的。如果有一个现有的dll/so文件, 需要使用JNI技术调用,我们首先需要另外使用C语言去编写一个dll/so共享库, 使用SUN规定的数据结构替代C语言的数据结构,调用已有的dll/so中公布的函数。然后在Java中载入这个适配器 dll/so,再编写Java Native函数作为dll/so函数中的代理,经过2个繁琐步骤才能在Java中调用本地代码。
因此, 很少有Java程序员愿意编写dll/so库中的原生函数的Java程序, 这也使Java语言在客户端上乏善可陈. 可以说JNI 是Java的一大弱点!
1.3、JNA相较于JNI的优缺点 JNA的优点:
学习成本低:JNA是基于JNI进行开发的,对外提供一套标准的Java Api。能够使从事Java开发的人员更快的接 收调用dll/so中的接口。 对接简单:通过JNA调用dll/so文件中的接口,只需要进行非常简单的名称映射和类型映射就可以直接调用 dll/so中的方法。
缺点:
性能较差:JNA相较于JNI性能较差,因为JNA是基于JNI进行开发。虽然JNA似的调用C冯家方便,但同时会导 致调用动态链接库的性能有所损失,所以如果对性能要求较高的可以酌情考虑是否使用JNA。 无法实现C调用Java代码:由于JNI的实现是在C和Java接口之间编写一套适配器,这就使得不仅仅可以实现 Java调用C接口,同样可以实现C调用Java接口。而JNA在实现简化Java调用C的过程中无法做到C调用Java接 口。
3、JNA的用法
/
- Copyright (C) 2013, Supcon. All rights reserved. *
- 文
件:QueryInterfaceAPI.h - 摘
*说 明:无 / #pragma onceinclude “QueryInterfaceDefine.h”
/ * 函 数 名 :IQI_Initialize - 说 明 :初始化,在调用接口前初始化一次即可
*参 数 :无 - 返 回 值 : TRUE 表示初始化成功;
- FALSE 表示初始化失败。 / VX_QUERY_API BOOL SC_STD_CALL IQI_Initialize();
/ * 函 数 名 :IQI_UnInitialize - 说 明 :反初始化,在调用完成所有接口后反初始化一次即可
参 数 :无
返回值: / VX_QUERY_API VOID SC_STD_CALL IQI_UnInitialize();
/ * 函 数 名 :IQI_CreateSession - 说 明 :用于创建会话
*参数: - [pServerID] - [in] 通过服务器ID,目前用IP代替,支持双网冗余
要:大型库组态接口 - 如:”172.30.1.129;172.31.1.129”
单网:如:”172.30.1.129”;双网,
如上是一段C的接口,如果需要使用Java进行调用,只需要实现如下代码
网络冗余和服务器冗余可以一起使用, 如:”172.30.1.129;172.31.1.129|”172.30.1.130;172.31.1.130””
[in] 用户名 /暂不支持 只需填NULL
- [in] /暂不支持 只需填NUL
- [out] 会话ID,在回调函数成功后有效
[in] 回调函数,判断是否连接成功的真正依据,不管成功还是失败,都会被
LPCSTR pUserName,
LPCSTR pPassword,
UINT32* pSessionID,
QISessionStatusFunc
LPVOID pCallBackParam );[pUserName]
- [pPassword]
- [pSessionID]
- [onSessionStatusChange] 调用。
[pCallBackParam]
*返 回 值 : / VX_QUERY_API INT32 SC_STD_CALL IQI_CreateSession( LPCSTR pServerID,
onSessionStatusChange,[in] 会在回调函数中传入参数
public interface QueryInterfaceMapping extends Library {
/**加载查询接口 /
QueryInterfaceMapping mapping = (QueryInterfaceMapping) Native.loadLibrary((Platform.isWindows() ? “VxQueryInterface” : “c”), QueryInterfaceMapping.class);
/*- 初始化DLL
- 在调用接口前初始化一次即可 *
- @return
/
boolean IQI_Initialize();
/* - 反初始化DLL
- 在调用完成所有接口后反初始化一次即可 *
- @return
/
boolean IQI_UnInitialize();
/* - 用于创建会话 *
- @param pServerID
- @param pUserName
- @param pPassword
- @param pSessionID
- @param onSessionStatusChange
【in】通过服务器ID,目前用IP代替,支持双网冗余 【in】用户名 /暂不支持 只需填NULL 【in】用户密码 /暂不支持 只需填NULL 【out】会话ID,在回调函数成功后有效
回调函数
如上是进行方法名映射,如果需要调用调用方式如下:
}
- @param pCallBackParam
- @return 返回0为成功,表示成功向服务端发送请求。其他返回表示失败,具体见返回值。 */
int IQI_CreateSession(String pServerID,
String pUserName,
String pPassword,
PSessionIdStruct.ByReference pSessionID, QISessionStatusFunc onSessionStatusChange, Pointer pCallBackParam);
public class QueryInterfaceApiImpl {
private QueryInterfaceMapping mapping = QueryInterfaceMapping.mapping;
public ExcResult initialize() { log.info(“位号查询接口,开始初始化DLL。”);
boolean result = mapping.IQI_Initialize(); log.info(“位号查询接口,初始化DLL。执行结果为:{}”, result); if (result) {
return ExcResult.success(); }
return ExcResult.error(); }
public ExcResult unInitialize() { log.info(“位号查询接口,开始反初始化DLL。”);
boolean result = mapping.IQI_UnInitialize(); log.info(“位号查询接口,反初始化DLL。执行结果为:{}”, result); if (result) {
return ExcResult.success(); }
return ExcResult.error(); }
public ExcResult createSession(String pServerID, QISessionStatusFunc
onSessionStatusChange) {
log.info(“位号查询接口,开始创建查询会话。”);
PSessionIdStruct.ByReference pSessionID = new PSessionIdStruct.ByReference(); int result = mapping.IQI_CreateSession(pServerID, vxQueryInterfaceUsername,
vxQueryInterfacePassword, pSessionID, onSessionStatusChange, null);
ExcResult excResult;
if (ExcResultStatusEnum.isSuccess(result)) { Session session = new Session(); session.setServerID(pServerID); session.setSessionID(pSessionID.pSessionID); excResult = ExcResult.successWithData(session);
} else {
excResult =
ExcResult.error(ExcResultStatusEnum.getExcResultStatusEnum(result)); }
除了按照如上进行简单的映射就可以使用,JNA还未C和Java提供了类型映射。
按照如上对应表进行类型映射。 4、JNA在TIMS系统中的应用
直接用代码演示
踩坑记
1、调用C接口返回的数据不正确 2、指针和组数的使用
3、线程与指针问题 4、C和Java类型在JNA中的映射关系 5、C语言中的无符号类型在JNA实现时的处理 6、C语言中的联合体应用 7、C接口中回调函数及回调函数中指针的使用
log.info(“位号查询接口,创建查询会话,执行结果为:result={},excResult={}”, result, JSON.toJSONString(excResult));
return excResult;
}
