JNI的初步认识-Java Native Interface
环境
Windows
Java
CMake
为什么要用 JNI & 简介
官网中对 JNI 的介绍如下
Java 本地接口规范:1 - 简介 — Java Native Interface Specification: 1 - Introduction
[!note]
The JNI is a native programming interface. It allows Java code that runs inside a Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly.While you can write applications entirely in Java, there are situations where Java alone does not meet the needs of your application. Programmers use the JNI to write Java native methods to handle those situations when an application cannot be written entirely in Java.
大意就是说,可以使用 Java 与其他编程语言进行交互,调用特定平台的原生代码。比如:
- 已有成熟的 C/C++库,不必重写
- 某些计算密集型任务对性能要求极高,优化性能
- 标准 Java 库无法实现相关功能
JDK 提供了 JNI,作为 JVM 字节码与原生代码(一般是 C/C++)之间的桥梁。
JNI 原理
native 方法
Java 提供了native关键字,用于声明一个方法的实现由外部提供。
1 | public native static void aNativeMethod(); |
这就相当于一个抽象方法,我们给出声明,而不实现。
假如由 C++提供,那么方法的实现就来自于一个动态链接库。
在 Windows 上是 dll 文件,在 Linux 上是 so 文件。
JNI 使用动态库,因为字节码和原生代码无法合并为单一二进制文件。
JNI 在运行时建立一张函数指针表,将 native 方法与动态链接库中的函数绑定,从而实现跨语言调用。
准备
要使用 JNI,必须有以下条件
- Java 代码:至少包含一个 native 方法的类
- 原生代码:C/C++实现 native 方法的逻辑
- JNI 头文件:
$JAVA_HOME/include/jni.h,定义了所有 JNI 接口 - C/C++ 编译器:如 GCC、Clang、MSVC,用于生成动态库
术语
Java
- native 声明一个方法为原生代码实现
System.loadLibrary("libname"):加载名为libname的动态库(无需加前缀lib或后缀.so/.dll)
C/C++(来自 jni.h)
JNIEXPORT:标记函数可被 JNI 导出JNICALL:确保函数调用约定与 JNI 兼容JNIEnv*:核心环境指针,提供操作 Java 对象、调用方法、转换类型等能力JavaVM*:JVM 实例指针,可用于启动/控制 JVM(高级用法)
详细的术语,参见官方文档,此处不作解释。
问题
既然 Java 可以调用 C++写的库,那么自然而然引出以下几个问题。
- 如何加载这个库?
- Java 和 C++的数据类型有差异,如何解决?
- Java 中的 native method 和 C++中的函数如何对应?
- 参数如何传递?
- C++代码怎么操作 Java 中的对象?
实践-(Hello World)
- 使用 C++以及 g++编译器
编写 Java 类
1 | public class HelloWorldJNI { |
生成 C++头文件并实现
1 | javac -h . HelloWorldJNI.java |
1 | /* DO NOT EDIT THIS FILE - it is machine generated */ |
1 |
|
参数说明:
JNIEnv* env:JNI 环境指针jobject thisObject:当前 Java 对象实例(即this)
编译&链接
我的%JAVA_HOME%是 D:\develop\jdk\include
编译命令为
1 | g++ -c -I "D:\develop\jdk\include" -I "D:\develop\jdk\include\win32" HelloWorldJNI.cpp -o HelloWorldJNI.o |
然后链接为 dll
1 | g++ -shared -o native.dll HelloWorldJNI.o |
运行
1 | java -cp . -Djava.library.path=. HelloWorldJNI |
可以看到,运行成功了。
注意
如果你在编写 Java 类时 native 方法为 static,那么生成的头文件也是不同的,第二个参数会从 jobject 变为 jclass。
总结
关于 JNI 的具体描述
Java Native Interface Specification: 2 - Design Overview
JNI 适用于一些特殊场景,但是也为此付出了代价。
数据类型复杂,跨语言通信开销大,而且失去了 Java 跨平台的优势。
使用起来难度很大。



