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 once

    include “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;
}