Java调用第三方库JNA(C/C++)

news/2024/11/25 15:38:54/

GitHub - java-native-access/jna: Java Native Access  源代码

 在Java 中使用C语言库的传统做法是使用JNI编程。但是现在有更好的替代方案,即JNA(Java Native Access);JNA是一个开源的Java框架,是SUN公司推出的调用本地库方法的技术,是建立在经典的JIN基础之上的一个框架,之所以说它是JIN的替代者,是因为它大大简化了调用本地方法的过程,使用很方便,基本上不需要脱离Java环境就可以完成.JNA只需要我们写java代码,而不用编写JNI或者本地代码(适配用的.dll/.so),只需要在JAVA中编写一个接口和一些代码,作为.dll/.so的代理。就可以在java程序中调用DLL/SO;

============================================================================

JNA调研成果,需求是公司同事用C++写了一个红外测温SDK,编译成so文件后提供给客户使用。客户需要一个Linux环境用Java调用so库的一个demo,刚好就我一个懂点Java,所有有了这次调研。

因为JNA相关资料实在太少,而且我一没用过Linux,二没搞过虚拟机,所以在研发过程中踩了太多坑,每向前迈一步都要克服很多困难,所以想记录下来,也许能给其他需要的人借鉴一下,少走一点弯路。Linux虚拟机Java开发环境我就不介绍了,这方面资料还是挺多的,主要说一下JNA的使用。

JNA介绍和技术原理
JNA全称Java Native Access,是一个建立在经典的JNI技术之上的Java开源框架。

JNA提供工具用于调用c/c++动态函数库(如Window的dll以及linux的so)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标函数库的函数与结构,JNA将自动实现Java接口方法到函数的映射。

JNA使用一个小型的JNI库插桩程序来动态调用本地代码。开发者使用Java接口描述目标本地库的功能和结构,这使得它很容易利用本机平台的功能,而不会产生多平台配置和生成JNI代码的高开销。这样的性能、准确性和易用性显然受到很大的重视。

此外,JNA包括一个已与许多本地函数映射的平台库,以及一组简化本地访问的公用接口。

注意:

JNA是建立在JNI技术基础之上的一个Java类库,它使您可以方便地使用java直接访问动态链接库中的函数。

原来使用JNI,你必须手工用C写一个动态链接库,在C语言中映射Java的数据类型。

JNA中,它提供了一个动态的C语言编写的转发器,可以自动实现Java和C的数据类型映射,你不再需要编写C动态链接库。

也许这也意味着,使用JNA技术比使用JNI技术调用动态链接库会有些微的性能损失。但总体影响不大,因为JNA也避免了JNI的一些平台配置的开销。

JNA数据类型映射表&模拟指针

在JNA中模拟指针,最常用到的就是Pointer类和PointerByReference类。Pointer类代表指向任何东西的指针,PointerByReference类表示指向指针的指针。Pointer类更加通用,事实上PointerByReference类内部也持有Pointer类的实例。
==================================================================

使用示例
环境说明:

jna-version:4.0.0(我用的是4.0.0版本)

jdk-version:1.8

java开发使用idea

JNA下载地址:https://github.com/java-native-access/jna.git

1、JNA简单调用
实现步骤
在Java类中创建一个接口CLibrary继承Library,

在CLibrary中加载so文件,

创建一个本地方法(对应so中的提供的native方法),

在main函数中用so实例(INSTANCE)调用本地创建的方法。

注意:下面是So库提供的可调用的方法.h头文件代码,JNA就是实现Java对这些函数的调用,后面的例子都是实现对这些方法的调用。
========================================================================

模拟OpenParams结构体,创建OpenParams继承Structure,设置成员变量,和C++结构体保持一致,参考映射表,char对应byte,int对应int,CapabilitySet也是结构体,属于结构体嵌套,CapabilitySet结构体模拟参考OpenParams,(JPEG_SIZE是枚举类型)C++中枚举类型和Java中不同,Java直接用int接收就可以。

创建两个内部类ByReference,ByValue继承OpenParams,分别实现接口Structure.ByReference、Structure.ByValue

覆盖getFieldOrder()方法,按顺序添加成员变量,一定要和C++结构体保持一致。

结构体传参分为值传递、引用传递(指针传递)

值传递使用ByValue

引用传递使用ByReference

在CLibrary中创建SCT_ChannelOpen(Pointer hChannel, OpenParams.ByRefrence openParams);
==========================================================================

从.h头文件中看到需要传入三个参数,hChannel指针,packetType枚举类型,param void指针,先看看C++中Packet Type,例如其中RTR_GetJpgFrame获取的数据param对应是一个FrameInfo结构体指针,所以要读取数据,我们要模拟结构体FrameInfo进行数据传递


============================================================================

现在模拟创建MtImgInfo结构体继承Structure,设置成员变量参考映射表,创建两个内部类ByReference、ByValue,获取数据,要想获取回调方法FunGetMtImgInfo()中其他数据,可以用同样的方法 

运行查看回调结果,成功输出回调结果

============================================================================

一、在使用springboot框架的时候,存在一个问题。就是我们配置yaml文件,需要单独提出来做参数修改。当然这个是可以通过spring.profiles.active的方式来配置dev,prod等环境的激活。但是我们如果存在环境不确定,或者需要启动脚本,启动项目的时候,这样通过jar的方式后续会处理很多工作。所以前期的集成工作还是很有必要的。

  二、这里有一个简单的例子,用于参数配置方式

  1)目录结构

2)需要的依赖包(pom.xml)

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>

3)maven的构建过程 

<build><finalName>assembly</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><includeSystemScope>true</includeSystemScope></configuration></plugin><plugin><!--主要使用的是maven提供的assembly插件完成--><artifactId>maven-assembly-plugin</artifactId><executions><execution><configuration><appendAssemblyId>false</appendAssemblyId><!--具体的配置文件--><descriptors>${project.basedir}/src/main/resources/assembly/package.xml</descriptors></configuration><id>make-assembly</id><!--绑定到maven操作类型上--><phase>package</phase><!--运行一次--><goals><goal>single</goal></goals></execution></executions></plugin></plugins></build>

 4)集成过程(package.xml)

<?xml version='1.0' encoding='UTF-8'?>
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"><!--打包名称,唯一标识--><id>${project.build.finalName}</id><!--打包格式,可以手动修改--><formats><format>tar.gz</format></formats><!--文件设置--><fileSets><fileSet><!--目标目录,会处理目录里面的所有文件--><directory>${project.basedir}/src/main/resources/config</directory><!--相对于打包后的目录--><outputDirectory>config</outputDirectory><!--文件过滤--><includes><include>*.*</include></includes></fileSet><fileSet><directory>${project.basedir}/src/main/resources/script</directory><outputDirectory>/</outputDirectory><includes><include>*.*</include></includes><!--文件权限--><fileMode>0755</fileMode><!--如果是脚本,一定要改为unix.如果是在windows上面编码,会出现dos编写问题--><lineEnding>unix</lineEnding></fileSet></fileSets><files><!--包含打包后的jar文件,可以不加入<outputDirectory/>,默认打包的目录--><file><source>${project.build.directory}/${project.build.finalName}.jar</source></file><!--这种方式也可以进行文件处理,但是针对单文件--><!-- <file><source>${project.basedir}/src/main/resources/script/start.sh</source><fileMode>0755</fileMode><lineEnding>unix</lineEnding></file>--></files>
</assembly>

备注:具体的参数的意义可以参考官网:Apache Maven Assembly Plugin – Assembly

5)通过maven的package打包


http://www.ppmy.cn/news/85439.html

相关文章

【计算机网络基础】辨析专题⑥ 应用层

文章目录 重要简写重要概念重要简写 域名系统——DNS文件传送协议——FTP简单文件传送协议——TFTP远程终端协议——TELNET万维网——WWW统一资源定位符——URL超文本传送协议——HTTP超文本标记语言——HTML可扩展标记语言——XML可扩展超文本标记语言——XHTML层叠样式表——…

图结构-图的数据表示法(java)

图结构 常见的图的数据表示法自定义一种数据结构来表示图。将一个线段集合转成我们自定义的图 常见的图的数据表示法 1.邻接矩阵表示法&#xff1a;用二维数组存储图的信息&#xff0c;数组中的对角线为0&#xff0c;表示不存在顶点到自身的边。若两个顶点之间有一条弧&#xf…

10款提效的在线设计工具推荐

在效率为王的时代&#xff0c;在线设计是设计的未来&#xff0c;为设计师提供了更节省时间、精力和成本的解决方案。 在线设计工具可以通过打开浏览器使用&#xff0c;大多数操作界面比传统设计工具更简单&#xff0c;入门门槛很低。 在这篇文章中&#xff0c;我们精心挑选了…

微信小程序的生命周期

微信小程序的生命周期 1.什么是生命周期&#xff1f;2.生命周期的分类3.什么是生命周期函数&#xff1f;4.生命周期函数的分类5.应用生命周期函数6.页面周期函数7.组件的生命周期函数lifetimes节点 8.组件所在页面的生命周期①什么是组件所在页面的生命周期&#xff1f;②pageL…

SeaTunnel本地运行以及kafka发送到redis说明

下载 Seatunnel2.3.1源码 Idea中的目录结构 编译 通过maven进行代码编译 编译命令 mvn clean package -pl seatunnel-dist -am -Dmaven.test.skiptrue 编译单个模块命令 mvn clean package -pl seatunnel-examples/seatunnel-engine-examples -am -Dmaven.test.skiptrue …

DJ8-3 shell 进程监控(ps、sleep、kill)

目录 8.6 进程监控 8.6.1 获取进程状态信息&#xff1a;ps 命令 8.6.2 暂停进程运行&#xff1a;sleep 命令 8.6.3 终止进程运行&#xff1a;kill 命令 8.6 进程监控 8.6.1 获取进程状态信息&#xff1a;ps 命令 1、不带参数的 ps 不带参数的 ps 命令运行时&#…

利用无代码工具开发一款小程序

目录 无代码工具开发小程序的流程需求分析阶段模型设计阶段页面搭建阶段创建项目创建数据表组件搭建 预览发布总结 日常我们开发小程序的时候都是要从写代码开始&#xff0c;但是写代码这个事只有专业开发才可以干&#xff0c;那作为普通人&#xff0c;如果也希望开发小程序&am…

企业级信息系统开发——初探Spring - 采用Java配置类管理Bean

文章目录 一、打开项目二、创建子包三、创建杀龙任务类四、创建勇敢骑士类五、创建Spring配置类六、创建测试类七、运行测试类八、总结Spring管理Bean的四种方式 一、打开项目 Maven项目 - SpringDemo 二、创建子包 在net.shuai.spring包里创建day04子包 三、创建杀龙任务…