文章目录
- 背景
- 介绍
- 什么是JNI?
- 什么是本地库?
- 开发Java使用JNI本地库步骤
- 编写Java类实现JNI本地调用
- windows系统下编译动态链接库
- 创建Java项目(demo)
- 第一步:编写带有native的Java类
- 第二步:javac生成`NativeDemo`类的字节码文件
- 第三步:javah根据字节码文件生成jni头文件(NativeDemo.h)
- 创建C++动态链接库(DLL)
- 第一步:打开Microsoft Visual Studio 2022创建项目
- 第二步:配置项目并创建
- 第三步:加载javah生成的NativeDemo.h头文件
- 第四步:配置jni.h导入找不到问题
- 第五步:编辑源码,导入`NativeDemo.h`头,实现对应方法。
- 第六步:执行本地windows调试器,生成.dll动态库文件
- 创建Java程序入口类,加载动态库并运行
- 第一步:创建Main.java文件,加载动态库,调用方法
- 第二步:编译Main.java、运行Main
- Linux系统下编译动态链接库
- 附录
- javac参数help
- 出现的一些问题
- 关于为什么在`javac`编译的时候要指定`-encoding`选项?
- 关于为什么要把NaviveDemo.h头文件复制到根目录后加载?
- 参考链接
背景
最近学习了Java中调用本地程序(JNI:Java Native Interface)、多种语言混合编程。但是关于Java怎么使用JNI调用本地程序是一点儿都不清楚,网上查阅了很多资料,编写了一个demo做简单调用机制的了解。
jdk自身在开发中也会调用自己的动态库(windows下的.dll文件),如下图:
介绍
什么是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结尾)
。
- 在windows系统下动态库以
.dll
结尾 - 在linux系统下动态库以
.so
结尾
开发Java使用JNI本地库步骤
- 编写Java类,方法要带有native关键字
- javac编译Java类,生成class文件
- javah生成native对应的头文件(头文件.h)
- C/C++实现javah生成的头文件,编译生成.dll动态库
- 动态库复制到java类的运行目录下,加载本地库后调用方法、运行
编写Java类实现JNI本地调用
windows系统下编译动态链接库
- 开发环境:
- windows 11系统
- jdk8
- Visual Studio Code IDE,编写Java代码
- Microsoft Visual Studio Community 2022 (64 位),编写C++代码,生成dll动态库
- 项目结构:
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
文件,生成字节码文件。
第三步:javah根据字节码文件生成jni头文件(NativeDemo.h)
生成的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,创建动态链接库。
第二步:配置项目并创建
第三步:加载javah生成的NativeDemo.h头文件
把javah生成的NativeDemo.h头文件复制到jni_dll
文件夹的根目录下。加载生成的头文件。
第四步:配置jni.h导入找不到问题
打开加载的NativeDemo.h头文件后,发现jni.h
、JNIEXPORT
和JNICALL
加载不到,需要对项目进行配置,导入jdk相关到项目,才能使用jni.h。
选择完成,应用后,导入的jni.h头文件不会报错了。
第五步:编辑源码,导入NativeDemo.h
头,实现对应方法。
打开pch.cpp源码文件,编辑。
// 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动态库文件
在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动态库到可运行文件所在目录,动态库会在此目录下加载。
Linux系统下编译动态链接库
待补充…
附录
javac参数help
出现的一些问题
关于为什么在javac
编译的时候要指定-encoding
选项?
大多数ide默认的编码环境是utf-8,windows命令提示符窗口,默认是GBK编码。如果使用javac命令直接编译,会出现字符不兼容的错误。所以在javac编译的时候指定使用utf-8的方式进行。
可以使用chcp
命令查看当前命令提示符窗口的编码方式。
修改编码方式后编译同样失败,可能的原因是当前窗口的编码对编译并不生效。
查阅了相关资料,如果直接对语言和区域的格式进行调整,可能会造成在dos窗口下导致其他字符显示错误。
关于为什么要把NaviveDemo.h头文件复制到根目录后加载?
在Microsoft Visual Studio 2022
版本编辑器下,加载的头文件不会直接导入项目,在使用#include 'NativeDemo.h'
导入头文件的时候,还是会出现找不到头文件的情况。
参考链接
- 在Java中调用C/C++本地库
- Windows: 转换cmd窗口的默认字符编码 - 夜行过客 - 博客园
- windows下vscode+vs2019开发JNI_vscode java jni 的 java.library.path-CSDN博客
- VS2019中C++,#include无法打开自己所写的头文件(.h)_vs2019 include报错-CSDN博客
- 静态库与动态库的区别与优缺点_静态库和动态库的优缺点-CSDN博客