Java中调用由C/C++实现的本地库(JNI本地程序调用)

背景

        最近学习了Java中调用本地程序(JNI:Java Native Interface)、多种语言混合编程。但是关于Java怎么使用JNI调用本地程序是一点儿都不清楚,网上查阅了很多资料,编写了一个demo做简单调用机制的了解。

        jdk自身在开发中也会调用自己的动态库(windows下的.dll文件),如下图:
1710482988613_B6C76C13-E923-4ee3-8E3F-8B3A78E99127.png

介绍

什么是JNI?

    JNI全称叫Java Navtie Interface,中文翻译本地调用。

C/C++是系统级的编程语言, 可以用来开发任何和系统相关的程序和类库, 但是Java本身编写底层的应用比较难实现, 使用JNI可以调用现有的本地库, 极大地灵活了Java的开发.
C/C++的效率是目前最好的语言, 可以使用C/C++来实现一些实时性非常高的部分. C/C++和Java本身都是非常流行的编程语言, 一些大型软件中经常使用语言之间的混合编程.
以上转载自:https://www.cnblogs.com/jaejaking/p/6840530.html(在Java中调用C/C++本地库)

什么是本地库?

本地库被分为静态库(.a和.lib结尾)动态库(.dll和.so结尾)

  1. 在windows系统下动态库以.dll结尾
  2. 在linux系统下动态库以.so结尾

开发Java使用JNI本地库步骤

  1. 编写Java类,方法要带有native关键字
  2. javac编译Java类,生成class文件
  3. javah生成native对应的头文件(头文件.h)
  4. C/C++实现javah生成的头文件,编译生成.dll动态库
  5. 动态库复制到java类的运行目录下,加载本地库后调用方法、运行

编写Java类实现JNI本地调用

windows系统下编译动态链接库

  • 开发环境:
    1. windows 11系统
    2. jdk8
    3. Visual Studio Code IDE,编写Java代码
    4. Microsoft Visual Studio Community 2022 (64 位),编写C++代码,生成dll动态库
  • 项目结构:

image.png

JNI_TEST
  --java-src   # java源码文件夹 & .dll动态库
  --jni_dll    # c++源码文件夹 & 生成的目标文件(.dll)

创建Java项目(demo)

第一步:编写带有native的Java类
/**
 * Java 本地native方法,具体由C++实现
 */
public class NativeDemo {
    /**
     * 由C++返回一个字符串,通过Java打印出来
     */
    public native String returnString();

}
第二步:javac生成NativeDemo类的字节码文件

打开命令提示符窗口,编译NativeDemo.java文件,生成字节码文件。

image.png

第三步:javah根据字节码文件生成jni头文件(NativeDemo.h)

image.png
        生成的NativeDemo.h,就需要#include 'NativeDemo.h'导入,通过C++去实现了。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class NativeDemo */

#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     NativeDemo
 * Method:    returnString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_NativeDemo_returnString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

创建C++动态链接库(DLL)

第一步:打开Microsoft Visual Studio 2022创建项目

搜索dll,创建动态链接库。

image.png

第二步:配置项目并创建

image.png

第三步:加载javah生成的NativeDemo.h头文件

        把javah生成的NativeDemo.h头文件复制到jni_dll文件夹的根目录下。加载生成的头文件。

image.png
image.png

第四步:配置jni.h导入找不到问题

        打开加载的NativeDemo.h头文件后,发现jni.hJNIEXPORTJNICALL加载不到,需要对项目进行配置,导入jdk相关到项目,才能使用jni.h。

image.png
image.png
选择完成,应用后,导入的jni.h头文件不会报错了。

第五步:编辑源码,导入NativeDemo.h头,实现对应方法。

打开pch.cpp源码文件,编辑。

image.png

// pch.cpp: 与预编译标头对应的源文件

#include "NativeDemo.h"
#include "pch.h"
#include <iostream>

// 当使用预编译的头时,需要使用此源文件,编译才能成功。
/*
 * Class:     NativeDemo
 * Method:    returnString
 * Signature: ()Ljava/lang/String;
 * 复制NativeDemo.h头中定义的方法,c++去实现
 */
JNIEXPORT jstring JNICALL Java_NativeDemo_returnString
(JNIEnv* env, jobject jobj) {

	return env -> NewStringUTF("exec dll: this is return from c++");
}

第六步:执行本地windows调试器,生成.dll动态库文件

image.png
        在jni_dll项目的根路径下,x64/Debug/jni_dll.dll就是生成的目标文件,后续被调用的本地动态库也是这个文件。

        到此,创建并生成.dll动态链接库已经完成。

创建Java程序入口类,加载动态库并运行

第一步:创建Main.java文件,加载动态库,调用方法

/**
 * 程序运行入口
 */
public class Main {

    static {
        /**
         * 加载动态库:只需要加载名字就可以,不需要带上.dll后缀,动态库放在和Main运行文件同一个目录下.
         * window上的动态库以.dll结尾,linux动态库以.so结尾
         */
        System.loadLibrary("jni_dll");
    }

    public static void main(String[] args) {
        // 创建Java对象,直接调用方法,具体的实现交给C++底层处理
        NativeDemo demo = new NativeDemo();
        // 调用方法, 由动态库制定并返回
        System.out.println(demo.returnString());

    }
}
第二步:编译Main.java、运行Main

        复制刚生成的jni_dll.dll动态库到可运行文件所在目录,动态库会在此目录下加载。

image.png

Linux系统下编译动态链接库

待补充…

附录

javac参数help

image.png

出现的一些问题

关于为什么在javac编译的时候要指定-encoding选项?

        大多数ide默认的编码环境是utf-8,windows命令提示符窗口,默认是GBK编码。如果使用javac命令直接编译,会出现字符不兼容的错误。所以在javac编译的时候指定使用utf-8的方式进行。

image.png
        可以使用chcp命令查看当前命令提示符窗口的编码方式。

image.png
        修改编码方式后编译同样失败,可能的原因是当前窗口的编码对编译并不生效。
        查阅了相关资料,如果直接对语言和区域的格式进行调整,可能会造成在dos窗口下导致其他字符显示错误。
image.png

关于为什么要把NaviveDemo.h头文件复制到根目录后加载?

        在Microsoft Visual Studio 2022版本编辑器下,加载的头文件不会直接导入项目,在使用#include 'NativeDemo.h'导入头文件的时候,还是会出现找不到头文件的情况。

参考链接

  1. 在Java中调用C/C++本地库
  2. Windows: 转换cmd窗口的默认字符编码 - 夜行过客 - 博客园
  3. windows下vscode+vs2019开发JNI_vscode java jni 的 java.library.path-CSDN博客
  4. VS2019中C++,#include无法打开自己所写的头文件(.h)_vs2019 include报错-CSDN博客
  5. 静态库与动态库的区别与优缺点_静态库和动态库的优缺点-CSDN博客
  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白说(๑• . •๑)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值