JNI本地方法调用初探
前言
在 java 中可以在方法中指定关键字 native 使得java可以调用本地代码中得方法,如c\cpp。
本文描述了如何简单的跑通了在java中调用本地的一个cpp方法的过程。
开始
工具
- IntelliJ IDEA 2022.3.2 编写java代码
- CLion 2023.2.2 编写cpp代码
java 代码
创建一个 main 文件
1 | public class Main { |
其中使用native 关键字,表示该方法为本地方法
构建该文件, 以下是文件树结构
1 | ├─out |
生成该本地方法的头文件
在项目的根目录的powershell或者cmd中使用以下命令:
1 | javah -classpath out/production/javaNative -d ./jni Main |
指令解释:
- javah: 表示要运行javah命令。javah命令用于生成Java类的本地方法接口(Native Method Interface,简称JNI)头文件。它可以根据Java类中声明的本地方法生成对应的C/C++头文件,以便在本地代码中实现这些方法。
- -classpath out/production/javaNative: 指定编译后的Java类文件所在的类路径。这里编译后的类文件位于out/production/javaNative目录中。
- -d ./jni: 指定生成的JNI头文件的输出目录。这里将生成的头文件放在当前目录下的jni目录中。
- Main: 指定要生成JNI头文件的Java类名。这里生成的JNI头文件将与名为Main的Java类相关联。
之后就能在 /jni 文件夹下看到 Main.h 的头文件
1 | /* DO NOT EDIT THIS FILE - it is machine generated */ |
cpp 代码
使用Clion
创建一个项目
在项目中的 CMakeList.txt中引入方才java编译使用的jdk的include目录和include/win32文件,例如:
1
2
3include_directories("C:/Program Files/Eclipse Adoptium/jdk-8.0.382.5-hotspot/include")
include_directories("C:/Program Files/Eclipse Adoptium/jdk-8.0.382.5-hotspot/include/win32")将 /jni 文件夹下看到 Main.h 的头文件 加入项目的根目录
1
2
3
4
5
6
7
8
9│ CMakeLists.txt
│ main.cpp
│ Main.h <---- 方在与main.cpp 同级别
│
├─.idea
│ ...
│
└─cmake-build-debug
...
编写cpp实现头文件中要求实现的方法
在 main.cpp 文件中写入:
1
2
3
4
5
6
JNIEXPORT jint JNICALL Java_Main_sum
(JNIEnv *, jclass, jint a, jint b){ //仅仅使用了后边的jint
return a+b;
}解释:
- JNIEXPORT jint JNICALL: 这是一个用于指定函数导出的宏定义。它告诉编译器将这个函数作为动态链接库的导出函数。JNIEXPORT是一个宏,用于指定函数的导出类型。jint表示函数的返回类型是int。
- Java_Main_sum: 这是一个约定的命名规则,用于表示这个JNI函数与Java类Main中的sum方法相对应。在JNI中,函数名的命名规则是Java_<类名>_<方法名>。
- (JNIEnv *, jclass, jint, jint): 这是函数的参数列表。在JNI中,函数的前两个参数分别是JNIEnv和jclass类型的指针,用于与Java虚拟机进行交互。后面的两个参数是jint类型的整数参数。
jni中的类型与Java的基本类型相似,仅仅需要在java的基本类型前加入j;例如Java中Boolean,在jni中则为jboolean;Byte<–>jbyte 。。。
编译生成动态库
在项目的根目录的powershell或者cmd中使用以下命令:
1 | gcc main.cpp -I "C:/Program Files/Eclipse Adoptium/jdk-8.0.382.5-hotspot/include" -I "C:/Program Files/Eclipse Adoptium/jdk-8.0.382.5-hotspot/include/win32" -shared -o main.dll -lstdc++ |
解释:
- gcc main.cpp 使用gcc编译main.cpp文件
- -I “…” 引入文件
- -shared 指定生成动态链接库
- -o main.dll 指定生成文件名字
- -lstdc++ 链接windows上的cpp标准库
最后就可以在项目的更目录到 main.dll 文件了
在Java中使用该dll文件
- 将生成的main.dll文件放在方才的java项目的更目录
- 在java Main.java 中加载该.dll文件,使用以下代码:
1
2
3
4
5
6
7
8
9
10
11
12public class Main {
static {
// 使用 System.getProperty("user.dir") 获取项目的路径
System.load(System.getProperty("user.dir") + "/mian.dll");
}
public static void main(String[] args) {
System.out.println(sum(114,514));
}
public static native int sum(int a,int b);
} - 编译运行得到结果:
1
2
3628
进程已结束,退出代码0
后话
尚不清楚 JNIEnv *, jclass 的作用,留有之后探究。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 AkiEvergarden!
评论