Java调用c++的动态链接库正确方式,踩坑选手!_java调用c++动态库-程序员宅基地

技术标签: jvm  c++  java  深入理解Java虚拟机  

前言,前几个月泛泛的看了一下c++的相关书籍。在看的过程中,做几个例子啥的没啥感觉。但是在整合起来的过程中还是发现了蛮多问题。

1、JNI是什么?需要怎么理解它?

JNI (Java Native Interface,Java本地接口)是一种编程框架 ,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。
有些事情Java无法处理时,JNI允许程序员用其他编程语言来解决。例如,Java标准库不支持的平台相关功能或者程序库。也用于改造已存在的用其它语言写的程序,供Java程序调用。许多基于JNI的标准库提供了很多功能给程序员使用,例如文件I/O、音频相关的功能。当然,也有各种高性能的程序,以及平台相关的API实现,允许所有Java应用程序安全并且平台独立地使用这些功能。
理解:JNI是一种约束和规范,我们可以通过java调用动态链接库来实现java不好处理的场景。

2、如何在idea和clion中操作使用?

我这边使用的环境是vs2019、idea2021、clion2019。其他的版本目前没尝试,我估计大概都差不多,书写到这里只是做了一个demo。
Demo的场景是,通过java的jni标准库调用c++动态联,获取float或double的二进制数据,并返回String打印!
我这里踩过的坑不少,提前梳理一下:

  1. 使用clion生成了动态联,但是idea上面报错,内容为没有找到哪些数据。
  2. 使用c++ char数据,在答应的过程中始终无法得到相关数据,在java中打印始终为“”。
  3. clion中的toolchains配置
  4. 配置cmakelist错误,始终无法编译

上面的问题足足折磨我了好几天,因为在过程中,我只是需要做个demo,跑完Hello World之后。想到一个场景,就想去证实一下,没想到……,下面我整理一下我的过程:

2.1Java中编写JNI接口

首先需要使用到JNI中的关键字native关键字,类似和java中的interface一样。eg:

public class FloatAndDouble2Binary {
    
    public native String float2Binary(float v_f);
    public native String double2Binary(double v_d);
    static {
    
        System.loadLibrary("doubleAndFloat2binary");
    }

    public static void main(String[] args) {
    
        double d=3.14;
        String s = new FloatAndDouble2Binary().double2Binary(d);
        System.out.println("double:"+s);
        float f=3.14f;
        String sf = new FloatAndDouble2Binary().float2Binary(f);
        System.out.println("float:"+sf);
    }
}

这样我们就写好了接口,但是如何转换成c++的头文件呢?__有两种方式:

2.1.1 通过指令自动生成

进入到源文件的目录,在搜索框中输入cmd,进入控制台:

javac -h . DFUtils.java

然后在目录里面你会发现多了两个文件:一个以.h结尾的文件,一个以.class结尾的二进制文件。
Ps:将.h的文件放入到一个文件夹中,这里提前说过的。做个伏笔

2.1.2 自己编写c++头文件__(c++头文件以.h,源文件则是.cpp)

这里看多了或者写过c++文件的人就知道如何做了。推荐使用指令生成!

3.1 使用Clion导入项目

3.1.1CmakeLists.txt文件的配置

打开Clion工具,点击New CMake Project from Sources,此时会自动生成一个CMakeLists.txt配置的文件。


cmake_minimum_required(VERSION 3.15)  #cmake 兼容最低的版本
project(double) #项目名
set(CMAKE_CXX_STANDARD 14) #cmake 编译的版本
include_directories(.) #include //包括的路径 可以理解为环境变量
include_directories(F:/include)
include_directories(F:/include/win32)
include_directories(F:/include/win32/bridge)
add_library(doubleAndFloat2binary SHARED
        com_company_float_double_class_FloatAndDouble2Binary.cpp
        com_company_float_double_class_FloatAndDouble2Binary.h)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
#add_excutable(项目名 待编译的项目.c 待编译的项目.h),与add_library不要同时使用
#否则会出现编译错,又要生成动态链接又要生成可执行的文件,是不行的!

如果使用jni的,你会得到提示jni无法找到的问题。这时候你需要到你自己jdk的配置目录找到include文件夹,其目录为<JAVA_HOME>/include
在CMakeLists.txt 中加入include_directories(path)即可。

3.1.2 安装toolchains

clion的步骤:File-Settings-Build,Execution,Deployment-ToolChains,我这里最开始安装的Cygwin,但是编译出来的时32为的动态连,在64位的机器上会出错。找了N多资料,还是没有找到,最终下载安装了VS。
安装VS的版本也是有要求的,如果在Toolchains里面提示过高过低,你可以降低或者升级vs的版本即可解决
Environment的目录为你安装vs的根目录,clion的Toolchains会自动搜索,其中Architecture,选择你对应cpu的版本,Platforms目前还不知道目的,有知道的朋友可以帮忙填空。Version自动会进行选择,

3.1.3 编写c++代码。

//com_company_float_double_class_FloatAndDouble2Binary.cpp
#include <jni.h>
#include "com_company_float_double_class_FloatAndDouble2Binary.h"
#include <iostream>

char* strChars;
template <typename T>
char* double2Template(T v);
char* value2Binary(char* point, int sof) {
    
    const int length = sof * 8+1;
    strChars = new char[length];
    int index = 0;
    for (int i = sof - 1; i >= 0; i--) {
    
        char item = point[i];
        for (int j = 7; j >= 0; j--) {
    
            strChars[index] = (item >> j & 1)+'0';
            index++;
        }
    }
    strChars[length-1]='\0';
    return strChars;
}

template<typename T>
char* double2Template(T v) {
    
    int sof = sizeof(v);
    const int lengt = sof * 8 ;
    char* point = (char*)(&v);
    char* arry = value2Binary(point, sof);
    return arry;
}

/*
 * Class:     com_company_float_double_class_FloatAndDouble2Binary
 * Method:    float2Binary
 * Signature: (F)Ljava/lang/String;
 */
JNIEXPORT jstring

JNICALL Java_com_company_float_1double_1class_FloatAndDouble2Binary_float2Binary
        (JNIEnv *env, jobject obj, jfloat f) {
    
    int byteLen = sizeof(jfloat);
    double2Template(f);
    return env->NewStringUTF(strChars);
}

/*
 * Class:     com_company_float_double_class_FloatAndDouble2Binary
 * Method:    double2Binary
 * Signature: (D)Ljava/lang/String;
 */
JNIEXPORT jstring

JNICALL Java_com_company_float_1double_1class_FloatAndDouble2Binary_double2Binary
        (JNIEnv * env, jobject obj, jdouble d) {
    
     double2Template(d);
    return env->NewStringUTF(&*strChars);
}

如果按照我上面的全部编写成功,可以使用快捷键ctrl+F9或者使用[Build-Build Project]进行编译。此时会看到一个.dll的文件。

在java中添加引用dll的文件

static{
    
	System.load("文件名");
}

将文件赋值到<Java_HOME>/bin目录下,直接运行java调用(也可以在idea中导入dll的动态链接库,网上有很多博客可以参考)。

4.1 Java如何直接将float或者double改成二进制呢?

        //使用java 得到float的二进制数
        float f=3.14f;
        int ibyte= Float.floatToIntBits(f);
        StringBuilder sb=new StringBuilder();
        for (int i=31;i>=0;i--){
    
            sb.append(ibyte>>i&1);
        }
        // 或者是直接遍历
        // Integer.toBinaryString(ibyte);
        System.out.println(sb);

至此Java调用c++动态库就成功了。一个简单的Hello World就此就完结了。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_20617983/article/details/130986074

智能推荐

虚拟机安装Mac Ox 10.6 之后的驱动安装vmware tool 安装_苹果笔记本装虚拟机后设备驱动也是虚拟吗-程序员宅基地

文章浏览阅读2.3k次。在虚拟机上成功安装Mac OX 10.6 系统之后,还有一些的需要的驱动。在安装vmare tool , 在启动界面,将setting->CD/DVD(IDE)选项中drawin.iso, 将上面的对话框connect 选中,之后会在系统的桌面上看到VMware Tool( Drawin) , 双击进去,直接双击Install VMware tools, 之后就可以的,重新启动,如下图所示_苹果笔记本装虚拟机后设备驱动也是虚拟吗

CentOS7安装docker 启动失败:Job for docker.service failed... 解决办法_job for docker.service failed because a fatal sign-程序员宅基地

文章浏览阅读7.5k次。https://blog.csdn.net/lixiaoyaoboy/article/details/82667510_job for docker.service failed because a fatal signal was delivered to the co

swagger访问路径_swaager访问路径-程序员宅基地

文章浏览阅读2k次。swagger访问路径:localhost:服务端口号/服务名/swagger-ui.html。_swaager访问路径

Jsp 表单如何设计_jsp页面表格设计-程序员宅基地

文章浏览阅读3.8k次。目录杂七杂八语法<%= %>相同点:不同点:语法< form >(生成表单)杂七杂八加!表示全局变量不加表示局部变量<% 这个中间是java代码 %>语法<%= %>表达式语法:<%= %>作用:向浏览器输出数据用jsp的表达式和Java输出的比较相同点:都是向浏览器输出数据不同点:1.语法范畴不同out.println()属于Java语法范畴 <% %><%= %>表达式输_jsp页面表格设计

(node-red)pm2的日志管理及使用pm2-logrotate进行日志分割_pm2 日志-程序员宅基地

文章浏览阅读1.8k次,点赞20次,收藏22次。通过pm2的日志管理插件(pm2-logrotate)实现node-red的日志分割管理_pm2 日志

运动耳机哪个品牌好?2022年值得入手的运动耳机推荐-程序员宅基地

文章浏览阅读150次。音质的表现还是很不错的,声音很清晰,听感有很大改善。越来越多人加入了运动中,运动不仅可以释放压力,还能加快新陈代谢,身体更加健康,当然,随着耳机越来越各样化,越来越难挑选,大家都不知道怎么找了,一款好的耳机其实并不难找,一般挑选耳机首先需要看它的防水性能高不高,高防水的耳机可以延长耳机的寿命,并且佩戴起来要稳固,运动的时候不会轻易甩掉,最后是音质的表现,下面我给大家分享几款还不错的运动耳机,看看有没有您喜欢的那款。采用的蓝牙5.0芯片,确保连接可以稳定,它的防水性能支持IPX4级,日常汗水是可以抵御的。

随便推点

spark-sql实现Kudu同步数据到mysql_sparksql 数据同步-程序员宅基地

文章浏览阅读1.7k次。Kudu同步数据到mysql实施方案简介目前kudu导出到mysql没有比较好的方案,临时借助spark-sql进行数据导出,处理逻辑是会把老的数据给删除再导入,已经完成了生产环境的上线。需要传入的参数程序参数 参数序号 字段含义 备注 1 同步的source表(含schema),必选 ..._sparksql 数据同步

Linux基础:xargs命令-I选项使用技巧_xargs -i-程序员宅基地

文章浏览阅读1.1w次,点赞3次,收藏17次。这篇文章使用具体示例来介绍一下xargs命令-I参数的常见使用方法。_xargs -i

[译]Kinect for Windows SDK开发入门(五):景深数据处理 下-程序员宅基地

文章浏览阅读60次。1. 简单的景深影像处理 在上篇文章中,我们讨论了如何获取像素点的深度值以及如何根据深度值产生影像。在之前的例子中,我们过滤掉了阈值之外的点。这就是一种简单的图像处理,叫阈值处理。使用的阈值方法虽然有点粗糙,但是有用。更好的方法是利用机器学习来从每一帧影像数据中计算出阈值。Kinect深度值最大为4096mm,0值通常表示深度值不能确定,一般应该将0值过滤掉。微软建议在开发中使用1220...

JavaScript 总结【面试笔记 + 经典面试题 ,超全前端中高级面试复习大纲-程序员宅基地

文章浏览阅读823次,点赞19次,收藏18次。阿里十分注重你对源码的理解,对你所学,所用东西的理解,对项目的理解。CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

python爬虫入门--odoo内应用_odoo爬虫-程序员宅基地

文章浏览阅读506次。此文章向大家介绍一个python入门级爬虫,本人也是初次尝试,如有缺陷,欢迎指正。_odoo爬虫

关于DevExpress2011.1.8版本和NetFramework4.5框架的不兼容问题解决方法-程序员宅基地

文章浏览阅读224次。问题:安装完Vs2012后,发现DevExpress WPF的GridControl控件就不能正常加载了。折腾了好久好久,找控件提供商都解决不了这个问题,报这个错误: ”对类型“DevExpress.Xpf.Grid.GridControl”的构造函数执行符合指定的绑定约束的调用时引发了异常。“解决办法: 经过google查找(不得不说这方面google还是更好!)安..._devexpress 11.1.8