基于JNI实现调用C++ SDK

news2024/11/23 10:45:48

基于JNI实现调用C++ SDK

  • 背景
  • 分析
  • 解决
  • 实践

背景

上篇文章总结了几种Java项目调用C/C++ SDK项目方法,在逐一实践、踩坑后,最终还是敲定采用 JNI 方式进行实现。在文章开始的过程,会先大概讲讲笔者遇到的情况,因为封装方式需要根据实际项目而定,可能不太适合于任何人。

分析

如图,这里列举了一个C++项目的头部文件,我们可以看到第一个方法,它返回的是一个GopHandle对象,而这个对象里有什么属性呢?笔者也不得而知,它的具体定义在一个被打包好的动态库里面,而且这里模板函数方式的格式,也让笔者无从下手。

在这里插入图片描述
这里总结了笔者遇到过的一些问题及解决方式:

  • C++模板函数请求的映射:目前通过各种渠道资源的搜索(chatgpt、google等社区)及外部求助,暂时还未找出好的解决方式。

  • 关于std::string &data_root 类型映射 :这个在用JNA实践过程中,在C++实际操作字符串时(如简单的读取值)会出现 invalid memory access 问题。猜测 &data_root 这种方式的传递,JNA传入的时string的地址,而C++ 对于这个地址的访问属于跨内存访问,去掉 & ,C++可正常访问

  • 对象指针的映射:虽然JNA提供了Pointer 指针类,但在实践过程中,这个 Pointer 对 基础类型(int、short、byte)的指针生效。对于对象类类型的指针,需要我们在Java层面,设计一个包含一摸一样的属性的 class 类去对应 C++ 的 结构体或者class 类。

以上图为例子,我们需要设计一个Java版本的 GopHandle类去对应 C++ 的 GopHandle,但GopHandle 封装的一个结构体或类,其中信息不得而知。

解决

这里GitHub找了很Java调用C++ SDK项目作参考,最终还是敲定 JNI 去实现,对于上面遇到的一些问题,也想出了解决思路:

  • 如果C++ 方法返回的指针,可以将其放到中间层,统一转为整形表示。(指针通常被表示为一个64位整数,可以用来存储内存地址。通过将指针转换为整数类型,可以在需要时保存或传递指针的值,而不需要直接操作指针。这点来自GPT,理性看待
  • 传参时,对于指针传参,统一放到中间层,用C去实现,Java还是采用对应的数据类型传输。
  • 关于模板函数的传参,同样统一放到中间层,用C去实现调用。

思路理清了,接下来就是撸代码了,以下是一些可能写得不太好C代码(现学现卖),但是能用。(手动狗头)

实践

  • Java项目里,根据头部文件定义本地方法:SentScore.java
    在这里插入图片描述
  • 生成头部文件
javac -h . SentScore.java
  • 用C 实现头部文件接口,在这里才去真正调用C++接口
  • 注意!!! 在这里对于内存的操作,不属于JVM的管理范畴,需要调用ReleaseStringUTFChars 手动释放内存,才不会有内存泄漏的风险。
  • 如下,是几个方法的例子:
 
#include "SentScore.h"
#include "SentScore.h"
 
 
#include "SentScoreSDK.h"
#include <vector>
 
 
#undef __request
#define __request EnglishSentPronsRequest
 
JNIEXPORT jlong JNICALL Java_SentScore_HandleCreate(JNIEnv *env, jclass obj, jstring data, jint num_threads){
    GopJniHandle *jni = new GopJniHandle(num_threads);
    if (jni){
        jboolean is_copy;
        // 实现Java里String ->  char *
        const char *data_root = env->GetStringUTFChars(data, &is_copy);
        // 调用SDK
        jni->handle = GopHandleCreate<__request>(data_root, num_threads);
        if (is_copy){
            // 释放内存
            env->ReleaseStringUTFChars(data, data_root);
        }
    }
    // 指针转整形
    return reinterpret_cast<jlong>(jni);
}
 
JNIEXPORT jint JNICALL Java_SentScore_HandleRelease(JNIEnv *env, jclass obj, jlong jni_gop_handle){
   // 整形转指针
    auto *jni = reinterpret_cast<GopJniHandle*>(jni_gop_handle);
    jint ans = 0;
    if (jni){
        if (jni->handle){
           ans = GopHandleRelease<__request>(jni->handle);
           jni->handle = 0;
        }
        delete jni;
    }
    return ans;
}
 
 
 
JNIEXPORT jint JNICALL Java_SentScore_ThreadHandleStarts(JNIEnv *env, jclass obj, jlong jni_gop_thread_handle, jstring jni_ref, jstring jni_utt, jfloat gop_adjust){
    auto *thread = reinterpret_cast<GopJniThreadHandle*>(jni_gop_thread_handle);
    __request request;
    request.result.gop_adjust = gop_adjust;
    if (thread && thread->thread){
    // 把传入的字符串jni_ref 复制到到text_buff
        CopyString(env, thread->text_buff, jni_ref);
        request.ref_text = thread->text_buff.data();
        CopyString(env, thread->text_buff, jni_utt);
        request.audio_id = thread->text_buff.data();
        return GopThreadHandleStarts<__request>(thread->thread, request);
    }
    else{
        jboolean a1, b1;
        const char *a = env->GetStringUTFChars(jni_ref, &a1);
        const char *b = env->GetStringUTFChars(jni_utt, &b1);
        request.audio_id = b;
        request.ref_text = a;
 
        if (a1) env->ReleaseStringUTFChars(jni_ref, a);
        if (b1) env->ReleaseStringUTFChars(jni_utt, b);
        return GopThreadHandleStarts<__request>(NULL, request);
    }
}
  • 最后一步,将上面的C代码跟着SDK 一起打包成动态库(.so),然后.so文件放到库路径下,在项目里引用调用。这里采用的框架是SpringBoot,直接在启动类里加加载库路径。
  • 关于库路径,可以打印如下代码进行获取,或者在程序里指定库路径:
System.getProperty("java.library.path")
  • 如果是Linux上,一般默认会库路径会有 /usr/bin,那么我们也可以通过创建软链接方式,把库文件放入库路径里。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1293153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文本润色工具有哪些,高质量的文本润色软件

在当今信息过载的时代&#xff0c;文本的重要性愈发凸显。即便是最精心构思的文章&#xff0c;若未经过仔细的润色&#xff0c;也难以达到最佳的表达效果。本文将专心分享文本润色工具的种类。 文本润色工具的种类 文本润色工具根据其功能和应用范围可以分为多个种类&#xff…

【C语言快速学习基础篇】之二控制语句、循环语句、隐式转换

文章目录 一、控制语句1.1、for循环1.2、while循环1.3、注意&#xff1a;for循环和while循环使用上面等同1.4、do while循环1.4.1while条件成立时1.4.2、while条件不成立时 C语言介绍 C语言是一门面向过程的计算机编程语言&#xff0c;与C、C#、Java等面向对象编程语言有所不同…

python pydoc生成API文档

pydoc是python内置的一个文档生成模块。 pydoc 模块会根据 Python 模块来自动生成文档。 生成的文档可在控制台中显示为文本页面&#xff0c;提供给 Web 浏览器访问或者保存为 HTML 文件。 对于模块、类、函数和方法&#xff0c;显示的文档内容取自文档字符串&#xff08;即 _…

202350读书笔记|《再别康桥:徐志摩诗选》——微风起,清芬酝藉,不减荼

202350读书笔记|《再别康桥&#xff1a;徐志摩诗选》——微风起&#xff0c;清芬酝藉&#xff0c;不减荼 《再别康桥&#xff1a;徐志摩诗选》我觉得有时候诗人是很狂热的&#xff0c;上头的感觉。 有几首很喜欢&#xff0c;节选如下&#xff1a; 偶然 我是天空里的一片云&…

【STM32】TIM定时器输入捕获

1 输入捕获 1.1 输入捕获简介 IC&#xff08;Input Capture&#xff09;输入捕获 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变时&#xff08;上升沿/下降沿&#xff09;&#xff0c;当前CNT的值将被锁存到CCR中&#xff08;把CNT的值读出来&#xff0c;写入到…

JavaScript编程基础 – 闭包(Closure)

JavaScript编程基础 – 闭包 JavaScript Programming Essentials - Closure By JacksonML 闭包和JavaScript的作用域有关。 我们需要先理解闭包的概念。 本文简要介绍闭包函数以及环境状态&#xff0c;并用实例说明闭包的创建及其基本用法。希望对学习及开发有所帮助。 1. …

sql优化常用的几种方法

SQL优化是数据库管理和应用开发中的一个重要环节&#xff0c;它涉及到对SQL查询语句的改进&#xff0c;以提高执行效率&#xff0c;减少系统负载&#xff0c;并优化资源使用。以下是一些常用的SQL优化方法&#xff0c;我会尽量覆盖一些关键的点。 使用合适的数据类型 选择适…

通俗易懂的案例+代码解释AOP 切面编程

目录 1. 理解AOP2 Before2.1 controller层2.2 service层2.3 自定义注解2.4 切面 advice 3 After4 Around spring的三大核心&#xff1a;IOC控制反转、DI依赖注入、AOP面向切面编程 刚开始接触springboot项目&#xff0c;前两个使用的多&#xff0c;亲自使用AOP的机会并不多&…

python自动化测试如何做数据缓存 ?这个第三方包推荐给你,方便又简单!

1.数据缓存说明 数据缓存可以说也是项目开发中比不可少的一个工具 &#xff0c;像我们测试的系统中 &#xff0c;你都会见到像Redis一样的数据缓存库 。使用缓存数据库的好处不言而喻&#xff0c;那就是效率高 &#xff0c;简单数据直接放在缓存中 &#xff0c;存取简单方便 。…

探索Scrapy-spider:构建高效网络爬虫

Spider简介 Scrapy中的Spider是用于定义和执行数据抓取逻辑的核心组件。Spider负责从指定的网站抓取数据&#xff0c;并定义了如何跟踪链接、解析内容以及提取数据的规则。它允许您定制化地指定要抓取的网站、页面和所需的信息。Spider的作用是按照预定的规则爬取网页&#xf…

12.7 C++作业

自由发挥登录窗口的应用场景&#xff0c;实现一个登录窗口界面 #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {//*******窗口相关设置********this->setWindowTitle("王者农药"); //设置窗口标题this->setWindowIcon(QIc…

OpenTiny Vue 3.12.0 发布:文档大优化!增加水印和二维码两个新组件

你好&#xff0c;我是 Kagol。 非常高兴跟大家宣布&#xff0c;2023年11月30日&#xff0c;OpenTiny Vue 发布了 v3.12.0 &#x1f389;。 OpenTiny 每次大版本发布&#xff0c;都会给大家带来一些实用的新特性&#xff0c;10.24 我们发布了 v3.11.0 版本&#xff0c;增加了富…

算法学习系列(六):高精度加法、减法、乘法、除法

目录 引言一、高精度加法1.题目描述2.代码实现3.测试 二、高精度减法1.题目描述2.代码实现3.测试 三、高精度乘法1.题目描述2.代码实现3.测试 四、高精度除法1.题目描述2.代码实现3.测试 引言 本文介绍了高精度加法、高精度减法、高精度乘法、高精度除法&#xff0c;这个高精度…

【Java数据结构 -- List和ArrayList与顺序表】

List和ArrayList与顺序表 一. List1.1 List介绍2.1 常见接口介绍3.1 List的使用 二. ArrayList与顺序表1.线性表2.顺序表2.1 接口的实现 3.ArrayList简介4. ArrayList使用4.1 ArrayList的构造 4.2 ArrayList常见操作4.3 ArrayList的遍历4.4 ArrayList的扩容机制5. ArrayList的具…

Python 网络爬虫(二):HTTP 基础知识

《Python入门核心技术》专栏总目录・点这里 文章目录 1. HTTP 协议简述2. HTTP 请求过程3. HTTP 的结构3.1 请求行3.2 请求头3.3 请求体3.4 状态行3.5 响应头3.6 响应体4. Cookie 状态管理5. HTTP 请求示例6. 总结<

HTML5+CSS3小实例:3D翻转Tab选项卡切换特效

实例:3D翻转Tab选项卡切换特效 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content=…

【Linux】公网远程访问AMH服务器管理面板

目录 1. Linux 安装AMH 面板2. 本地访问AMH 面板3. Linux安装Cpolar4. 配置AMH面板公网地址5. 远程访问AMH面板6. 固定AMH面板公网地址 AMH 是一款基于 Linux 系统的服务器管理面板&#xff0c;它提供了一系列的功能&#xff0c;包括网站管理、FTP 管理、数据库管理、DNS 管理、…

信号完整性分析

目录 前言一、信号完整性SI1.1 信号失真1.2 串扰1.3 衰减 二、电源完整性PI2.1 地弹2.2 电源轨道塌陷 三、电磁兼容EMC3.1 电磁辐射3.2 抗干扰 前言 本篇介绍信号完整性分析的知识体系&#xff0c;以及部分分析方法。   什么是信号完整性?通俗来讲&#xff0c;信号在互连线的…

若依角色与权限字符串

文章目录 一、简介1.基于权限字段串2.基于角色 二、若依的权限控制1.介绍2.实践 一、简介 基于权限字段串和基于角色的访问控制是两种不同的权限管理模型&#xff0c;它们各自有其优点和应用场景。下面是这两种模型的基本概念&#xff1a; 1.基于权限字段串 基于权限字段串&…