java--通过JNI调用动态链接库(dll、so、dylib)

2023-05-16

公司项目需要用到第三方qt插件,由于业务是偏底层的,基本上用不到jar包,因此只能通过jni的方式调用。没学过c++,十多天的摸爬滚打一路过来不容易啊!今天总算跑通了。网上关于jni的资料相关博客有很多,我这里就不重复了,推荐两个博主的文章,我主要把碰到比较困难的问题总结一下。

推荐博客:

https://www.cnblogs.com/roccheung/p/5797415.html

https://blog.csdn.net/honjane/article/details/53716235

问题1:

首先是参数传递的问题。思路很清晰,java这边定义一个native方法,然后这个方法在c++(qt)那方实现,很容易就能想到参数传递以及返回值是关键,只要搞清楚这两点剩下的逻辑在qt实现就行了。基本传递数据类型比较简单,对象类型通过上面两个系列的文章也能搞定,碰到的第一个问题是复杂对象类型数据的传递。比如类A的参数有int id,B b,C c,即有的字段是对象类型的。甚至再多嵌套几重也是一样的想清楚了还是比较简单的。举个栗子:

//真正实现的时候字段肯定是private,这里只是方便演示(好吧,我摊牌了,少写两行代码-_-..)
//private字段通过get set方法一样能拿到数据。
public class A{
	public int id;
    //这里如果是human数组或者list也可以,就不搞那么复杂了
	public Human human;
	public Giraffe giraffe;
	
	//此方法的功能:传入a对象,计算Human 和Giraffe 的身高差
	public native static float div(A a);

    public A(Human human, Giraffe giraffe) {
        this.human = human;
        this.giraffe = giraffe;
    }
}


class Human{
	public String name;
	public float tall;
}

class Giraffe{
	public String name;
	public float tall;
}

将A.java生成头文件com_zone_jni_A.h后看到最主要的地方:

/*
 * Class:     com_zone_jni_A
 * Method:    div
 * Signature: (Lcom/zone/jni/A;)F
 */
JNIEXPORT jfloat JNICALL Java_com_zone_jni_A_div
  (JNIEnv *, jobject, jobject);

新建个Sources文件main.cpp实现这个方法。

#include "com_zone_jni_A.h"
#include <iostream>

/*
* Class:     com_zone_jni_A
* Method:    div
* Signature: (Lcom/zone/jni/A;)F
*/
JNIEXPORT jfloat JNICALL Java_com_zone_jni_A_div
(JNIEnv *env, jclass, jobject jAObject){
	//获取jAObject对象中的class对象
	jclass aClass = env->GetObjectClass(jAObject);
	//获取aClass对象中名称为'id'签名为I的字段id
	jfieldID fieldId = env->GetFieldID(aClass, "id", "I");
	//得到jAObject对象的id字段的值
	jint getId = env->GetIntField(jAObject, fieldId);
	std::cout << "id---" << getId << std::endl;

	//查找HumanClass
	jclass jHumanClass = env->FindClass("com/zone/jni/Human");
	//字段名称:tall 签名:Lcom/zone/jni/Human;   ###注意 ";" 是属于签名的
	jfieldID id_human = env->GetFieldID(aClass, "human", "Lcom/zone/jni/Human;");
	jobject getHuman = env->GetObjectField(jAObject, id_human);
	jfieldID id_human_tall = env->GetFieldID(jHumanClass, "tall", "F");
	jfloat humanTall = env->GetFloatField(getHuman, id_human_tall);

	//这里用不上name字段,只是说明一下jstring和c++的string是不同的需要做转换
	jstring humanName = (jstring)env->GetObjectField(getHuman, id_human);
	const char *cHumanName = env->GetStringUTFChars(humanName, nullptr);
	std::cout << "HumanName: " << cHumanName << std::endl;

	//查找GiraffeClass
	jclass jGiraffeClass = env->FindClass("com/zone/jni/Giraffe");
	//字段名称:tall 签名:Lcom/zone/jni/Giraffe; 
	jfieldID id_giraffe = env->GetFieldID(aClass, "giraffe", "Lcom/zone/jni/Giraffe;");
	jobject getGiraffe = env->GetObjectField(jAObject, id_giraffe);
	jfieldID id_giraffe_tall = env->GetFieldID(jGiraffeClass, "tall", "F");
	jfloat giraffeTall = env->GetFloatField(getGiraffe, id_giraffe_tall);

	return giraffeTall - humanTall;
}

java测试:

问题2:

常见的崩溃和错误:

一、崩溃

二、错误

Exception in thread "main" java.lang.UnsatisfiedLinkError:

java.lang.NoSuchFieldError: id

java.lang.NoSuchMethodError: id

.......

错误的解决不要怀疑,一定是代码的问题。

崩溃大多数原因也和代码相关(是指一些粗心或者意外写错的代码,学java的,指针,内存溢出那些没算在这一类),还有一些比较复杂,原因也千奇百怪,比如内存溢出、软件版本等问题。

因此排查问题的第一步就是:检查代码!检查代码!检查代码!确定c++调用java的类字段方法要一模一样。

从上到下说来看:这三个类我都放在同一个包下,com.zone.jni。

在c++中FindClass或者需要用到签名的地方着重检查时不时对应的,如果修改了包名这些用到的地方一定要同步修改。

再看到字段,java定义了id字段:public int id;小写 i,c++如果大写也会出错,像这样:

方法名也要一样不用多说了。

再下来到方法调用传参的地方,c++用到了的参数(对象)就必须赋值,例如下面这样:

用到了A对象中的human、giraffe等值,而又传的空对象过去,肯定是不行的。

对于jvm崩溃的一些复杂的情况后面再讨论。

问题3:

Can't find dependent libraries问题

当你的dll文件明明放在指定文件夹或者项目文件下,不管用绝对路径还是相对路径都报错找不到libraries的时候就要检查一下依赖了,如下图。利用工具Dependency Walker

工具链接:

链接:https://pan.baidu.com/s/1CNuLHsSHM8JsjnIqgbiMAg 
提取码:iek1 

如QtConverLibTest.dll依赖的其他dll:

黄色问好表示缺少该依赖,拿QT5Cored.dll来说,它下面可能还依赖了很多dll,要一层一层导入并且被依赖的dll要在依赖的dll之前被导入。

图示QT5Cored.dll依赖的ICUDT52.DLL 、ICUUC52.DLL、ICUIN52.DLL在它之前被导入。

当然要是这样一行一行写下来如果依赖的库多了,写二三十行都很常见,那么怎么办呢,不可能每次都写那么多load吧。那就是修改环境变量,把dependency路径加在PATH环境变量中,和设置jdk环境变量一样的方法,很简单,不会的另行百度。设置了环境变量后就只需要加载那一个你需要的dll了。

问题4:

Debug下正常, Release崩溃的问题

debug顺利的调试通了,千万别高兴的太早,这个问题足足困扰了我两天半,虽然最后解决了,但还是不清楚具体原因。为了找问题基本上是一段代码一段代码的debug,定位到了几个地方却发现代码没问题。网上有很多种说法,大多都是说内存问题、野指针、数组越界、初始化等等。起初怀疑是QString.arg()有问题,还有jstring转char*,但调查后都没看出什么所以然,最后是通过将LPCWSTR类型的变量改为了char*,只能感叹c++中字符串的操作比起java来好复杂。

参考:

https://blog.csdn.net/Qsir/article/details/78645062

问题5:

LInux下文件权限的问题。

如果你的代码中用了QProcess *process = new QProcess();调用本地的应用程序那么一定要检查应用程序的权限及你生成的动态库文件的权限。最好直接将权限设置为755,以防检查问题的时候没看到这点,耽误很久来查问题。

问题6:

其他

1、其实这一点算不上什么问题,只是需要注意:不同平台不同环境要对应,32位jdk使用32位dll,64位jdk使用64位dll,mac环境下生成.dylub文件,linux生成.so文件。

2、关于测试。要是linux和mac机器上没有idea等编辑器,只需要装好一个jdk也是可以测试的。毕竟java号称一次编译到处运行可不是白来的。只需要将测试类打成jar包即可。生成jar包可以参考:

https://blog.csdn.net/zhanaolu4821/article/details/103006983

其实只需要记住一句话:jar -cvf  xxx.jar  com/

普通是java类打成jar包默认是没有指定主类的(SpringBoot等项目可以在maven中配置),要是碰到如下问题:

需要用压缩工具打开jar包解压出MANIFEST.MF文件,然后修改

如下图:

加上这句话Main-Class: xxx  (根据自己的主类而定)

最后将此文件复制进jar包替换即可。生成了jar包就可以在各个环境下测试了。

3、此外就是对linux和mac环境的基本操作要熟练。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java--通过JNI调用动态链接库(dll、so、dylib) 的相关文章

  • 按下按钮并在java中的新窗口中打开文件

    我创建了一个 JFrame 并放置了一个文本字段和按钮 在文本字段中我放置了从文本文件读取的名称 我知道我想单击按钮并打开一个已知窗口 我想在其中放置名称 其他信息来自同一个文件 这是我的代码 这是我的主框架 package Fronten
  • 使用 Java 的 Apache Http 摘要身份验证

    我目前正在开发一个 Java 项目 但无法使 http 摘要身份验证正常工作 我尝试使用 Apache 网站 但没有帮助 我有一个需要 HTTP 摘要身份验证的网站 DefaultHttpClient httpclient new Defa
  • Java:扩展类并实现具有相同方法的接口

    可能无法完成以下操作 我收到编译错误 继承的方法 A doSomthing int 无法隐藏 B 中的公共抽象方法 public class A int doSomthing int x return x public interface
  • Java Logger 未记录到 Netbeans 中的输出

    我正在 Netbeans 中使用 Maven 启动一个 Java 项目 我编写了一些代码来使用 Logger 类进行日志记录 但是 日志记录似乎不起作用 在程序开始时 我运行 Logger getLogger ProjectMainClas
  • 两个整数乘积的模

    我必须找到c c a b mod m a b c m 是 32 位整数 但 a b 可以超过 32 位 我正在尝试找出一种计算 c 的方法 而不使用 long 或任何 gt 32 位的数据类型 有任何想法吗 如果m是质数 事情可以简化吗 注
  • 如何在 JPQL 或 HQL 中进行限制查询?

    在 Hibernate 3 中 有没有办法在 HQL 中执行相当于以下 MySQL 限制的操作 select from a table order by a table column desc limit 0 20 如果可能的话 我不想使用
  • Mockito 使用 @Mock 时将 Null 值注入到 Spring bean 中?

    由于我是 Spring Test MVC 的新手 我不明白这个问题 我从以下代码中获取了http markchensblog blogspot in search label Spring http markchensblog blogsp
  • Sun 在 EDT 之外做 GUI 工作的演示?

    我正在看SplashDemo java http download oracle com javase tutorial uiswing examples misc SplashDemoProject src misc SplashDemo
  • 从jar中获取资源

    我有包含文件的 jar myJar res endingRule txt myJar wordcalculator merger Marge class 在 Marge java 中我有代码 private static final Str
  • 提高 PostgreSQL 1 亿数据左连接查询性能

    我在用Postgresql 9 2 version Windows 7 64 bit RAM 6GB 这是一个Java企业项目 我必须在我的页面中显示订单相关信息 有三个表通过左连接连接在一起 Tables TV HD 389772 行 T
  • Jetty、websocket、java.lang.RuntimeException:无法加载平台配置器

    我尝试在 Endpoint 中获取 http 会话 我遵循了这个建议https stackoverflow com a 17994303 https stackoverflow com a 17994303 这就是我这样做的原因 publi
  • 如何将 HTML 链接放入电子邮件正文中?

    我有一个可以发送邮件的应用程序 用 Java 实现 我想在邮件中放置一个 HTML 链接 但该链接显示为普通字母 而不是 HTML 链接 我怎样才能将 HTML 链接放入字符串中 我需要特殊字符吗 太感谢了 Update 大家好你们好 感谢
  • 如何在JPanel中设置背景图片

    你好 我使用 JPanel 作为我的框架的容器 然后我真的想在我的面板中使用背景图片 我真的需要帮助 这是我到目前为止的代码 这是更新 请检查这里是我的代码 import java awt import javax swing import
  • 在 Java 中获取并存储子进程的输出

    我正在做一些需要我开始子处理 命令提示符 并在其上执行一些命令的事情 我需要从子进程获取输出并将其存储在文件或字符串中 这是我到目前为止所做的 但它不起作用 public static void main String args try R
  • 轻松的反应

    我有一个与这里描述的类似的案例 动态更改RESTEasy服务返回类型 https stackoverflow com questions 3786781 dynamically change resteasy service return
  • 手动设置Android Studio的JDK路径

    如何为 Android Studio 使用自定义 JDK 路径 我不想弄乱 PATH 因为我没有管理员权限 是否有某个配置设置文件允许我进行设置 如果您查看项目设置 您可以从那里访问 jdk 在标准 Windows 键盘映射上 您可以在项目
  • java XMLSerializer 避免复杂的空元素

    我有这个代码 DocumentBuilderFactory factory DocumentBuilderFactory newInstance DocumentBuilder builder factory newDocumentBuil
  • 子类构造函数(JAVA)中的重写函数[重复]

    这个问题在这里已经有答案了 为什么在派生类构造函数中调用超类构造函数时 id 0 当创建子对象时 什么时候在堆中为该对象分配内存 在基类构造函数运行之后还是之前 class Parent int id 10 Parent meth void
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • Spring RESTful控制器方法改进建议

    我是 Spring REST 和 Hibernate 的新手 也就是说 我尝试组合一个企业级控制器方法 我计划将其用作未来开发的模式 您认为可以通过哪些方法来改进 我确信有很多 RequestMapping value user metho

随机推荐

  • Bat脚本-编译及烧录keil-MDK工程

    目录 概述背景需求需求分析需求实现实现功能使用示例1 修改脚本中的编译软件路径2 将脚本的文件路径加入系统的环境变量3 通过Vscode 进行调用 可进一步优化的方向总结 概述 本文主要讲述的是一个关于调用keil MDK的软件编译对应单片
  • ubuntu部署http服务器

    一 安装apache2 sudo apt install y apache2 二 配置环境 默认是80端口 xff0c 防止冲突自定义端口 xff1a 8001 1 修改 etc apache2 ports conf文件j监听端口 List
  • c/c++math.h标准库

    math h数学函数 这个库中所有可用的函数取double参数并返回double的结果 abs 绝对值 acos 反余弦 acosh 反双曲余弦 asin 反正弦 asinh 反双曲正弦 atan2 两个参数的反正切 atan 反正切 at
  • Linux Simulink打不开——unable to run the MATLABWindow application on Linux

    Linux Simulink打不开 unable to run the MATLABWindow application on Linux 解决办法 xff1a 解决办法 xff1a 参考网页 xff1a https www mathwor
  • RTX移植STM32F103,超详细~

    目录 RTX移植前言移植1 创建工程2 配置工程3 添加LED文件4 编写测试代码5 编译烧录 RTX移植 往期回顾 为什么需要RTOS 前言 keil RTX是一款应用广泛的嵌入式RTOS xff0c 具体可见官网 为什么需要RTOS x
  • 基于28035的ePWM触发ADC采样设计

    目录 前言实验目的实验要求硬件电路实验步骤代码解释实验结果总结体会 前言 玖道最近在做一个开关电源项目 xff0c 需要用到TI 的TMS320F28035 芯片 xff0c 实现控制电路的设计 简单来说就是利用28035采集信号量 xff
  • 浅析DSP28035的看门狗模块

    目录 背景工作原理结构与寄存器代码实验结果总结 背景 嵌入式系统通常会使用DSP来实现控制功能 xff0c 比如开关电源的数字控制 试想如果DSP中程序出现Bug 崩溃 跑飞了怎么办 xff1f 如果不及时处理 xff0c 这对整个嵌入式系
  • 浅析TMS320F28035的GPIO模块

    目录 GPIO简介硬件电路软件代码总结 通用输入输出端口 General Purpose Input Output GPIO 几乎是学习包括DSP xff0c 单片机在内的微处理器的第一步 GPIO的功能包括基本的输入 xff0c 输出 x
  • Ubuntu 20.04下配置 HAL 汇编开发环境教程

    目录 安装解压配置环境变量测试 案例 安装 环境 xff1a Ubuntu 20 04 安装包 xff1a linux64 hla tar gz 这里安装的是64位的 xff0c 具体与linux hla tar gz有什么不同 xff0c
  • 为什么单片机需要时钟系统,时钟信号在单片机中扮演怎样的角色?

    现在想想 xff0c 我自学单片机都快一年了 我自学过8051 msp430和STM32 xff0c 做一些基本的小项目 xff0c 参加过2018年江苏省电子设计大赛 xff0c 还获得了一等奖 我懂 xff0c 也不懂单片机 不懂就要去
  • Spring Security OAuth2.0认证授权学习与使用~(更新中)

    Spring Security OAuth2 0认证授权学习与使用 1 1 什么是认证1 1 1 系统为什么要认证 xff1f 1 1 2 认证 1 2 什么是会话 xff1f 1 2 1 会话1 2 2 基于Session的认证方式如下图
  • Altium中PCB上走线镀锡的方法

    Altium中PCB上走线镀锡的方法 PCB设计时 有时候需要在不增加PCB走线宽度的情况下提高该走线通过大电流的能力 载流能力 通常的方法是给该导线镀锡 或者上锡 下面以在PCB顶层走线镀锡为例 使用AD09软件 简单介绍如何走线上锡处理
  • Arduino成长日记2 - Arduino编程基础

    上一篇讲述了什么是Arduino以及各类Arduino开发板的参数 xff0c 本篇开始介绍开发环境搭建以及一些编程基础 Arduino开发环境 开发环境即Arduino项目的编程环境 Arduino IDE xff0c Arduino集成
  • 通过QGC应用TFmini Plus

    TFmini QGC中所需设置参数 xff1a EKF2 RNG AID enable EKF2 RNG MODE Range sensor SENS TFMINI CFG TELEM SERIAL 4 最后一个参数如何确定是哪一通道 xf
  • C语言格式化字符串漏洞实验

    格式化字符串漏洞实验 在线实验环境 xff1a 格式化字符串漏洞实验 文章转载自 xff1a https github com shiyanlou seedlab blob master formatstring md 一 实验描述 格式化
  • C语言Socket编程(TCP与UDP)

    C语言Socket编程 xff08 TCP与UDP xff09 UDP xff1a span class token comment udp server c span span class token macro property spa
  • ubuntu 换源深层次解析

    换源也是一个容易出错的问题 xff0c 本文以树莓派为例展开 xff0c x86也是一样的操作 那么假设成立的话 xff0c 就要记住我们是在树莓派 xff08 arm xff09 上安装的ubuntu xff0c 不是X86 xff0c
  • 1运动规划概述

    Motion planning autonomous robot 总结一下什么是自主机器人 xff1a 首先我们需要状态估计 xff08 定位 xff09 xff1b 基于此 xff0c 利用传感器融合人建立一个周围环境的三维地图 xff1
  • 5轨迹生成

    文章目录 Introduction全局与局部方法全局与局部对比 轨迹生成T xff08 what xff09 为什么需要平滑轨迹呢Y why W how 微分平坦 xff08 Differential Flatness xff09 TQua
  • java--通过JNI调用动态链接库(dll、so、dylib)

    公司项目需要用到第三方qt插件 xff0c 由于业务是偏底层的 xff0c 基本上用不到jar包 xff0c 因此只能通过jni的方式调用 没学过c 43 43 xff0c 十多天的摸爬滚打一路过来不容易啊 xff01 今天总算跑通了 网上