JNI 实现原理
Java Native Interface(JNI)是 Java 用于调用本地(Native)代码的接口,使得 Java 可以和 C/C++ 等编程语言编写的代码进行交互。JNI 通过以下步骤进行工作:
- 声明本地方法:在 Java 代码中声明本地方法(native),标记方法将由本地代码来实现。
- 动态加载库:Java 程序在运行时加载本地库,通过
System.loadLibrary
方法来加载动态链接库。 - JNI 头文件:使用
javah
或javac -h
生成 JNI 头文件(.h
),生成的头文件包含了 Java 调用本地方法的接口。 - 编写本地代码:在本地语言(如 C/C++)中实现头文件中声明的接口函数。这些函数名称和签名必须和头文件一致。
- 编译本地代码生成动态库:将本地代码编译为动态链接库(例如 Linux 上
.so
文件,Windows 上.dll
文件,macOS 上.dylib
文件)。 - 调用本地方法:Java 程序调用
native
方法时,JVM 会通过 JNI 调用本地方法的实现,并返回结果。
示例代码
我们将编写一个简单的 Java 程序调用 C 代码,输出 “Hello from C!”.
1. 创建 Java 类并声明本地方法
public class JNIDemo {// 声明一个本地方法 sayHellopublic native void sayHello();// 加载动态链接库static {System.loadLibrary("JNIDemoLib"); // 加载库 JNIDemoLib.so 或 JNIDemoLib.dll}public static void main(String[] args) {// 调用本地方法new JNIDemo().sayHello();}
}
在这里,我们定义了一个 native
方法 sayHello
,并加载一个名为 JNIDemoLib
的动态链接库。
2. 生成 JNI 头文件
编译 Java 类文件并生成 JNI 头文件。
# 编译 Java 文件
javac JNIDemo.java# 生成 JNI 头文件
javac -h . JNIDemo.java
执行完后,会生成 JNIDemo.h
头文件。头文件内容类似于以下结构:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#ifndef _Included_JNIDemo
#define _Included_JNIDemo
#ifdef __cplusplus
extern "C" {
#endif
/** Class: JNIDemo* Method: sayHello* Signature: ()V*/
JNIEXPORT void JNICALL Java_JNIDemo_sayHello(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif
其中 Java_JNIDemo_sayHello
函数名由 类名_方法名
构成,这也是 JNI 规定的命名规则。
3. 编写 C 实现代码
创建 JNIDemo.c
文件,实现 sayHello
函数。
#include <jni.h>
#include <stdio.h>
#include "JNIDemo.h"// 实现 sayHello 函数
JNIEXPORT void JNICALL Java_JNIDemo_sayHello(JNIEnv *env, jobject obj) {printf("Hello from C!\n");
}
这个函数是 sayHello
方法的 C 实现,通过标准输出打印出 “Hello from C!”。
4. 编译 C 代码生成动态链接库
将 JNIDemo.c
文件编译为动态库。
-
Linux / macOS:
gcc -shared -fpic -o libJNIDemoLib.so JNIDemo.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux
-
Windows:
gcc -shared -o JNIDemoLib.dll JNIDemo.c -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32"
注意:请根据实际的 JDK 安装路径替换
${JAVA_HOME}
或"%JAVA_HOME%"
。
编译完成后,将会生成动态链接库文件 libJNIDemoLib.so
(在 Linux/macOS)或 JNIDemoLib.dll
(在 Windows)。
5. 运行 Java 程序
确保生成的动态库(libJNIDemoLib.so
或 JNIDemoLib.dll
)在 java.library.path
路径中(可以将库文件放到项目运行的根目录),然后执行 Java 程序。
java JNIDemo
如果成功执行,将会在控制台输出:
Hello from C!
总结
使用 JNI 调用本地库的关键步骤:
- 在 Java 中声明
native
方法。 - 使用
System.loadLibrary
加载动态链接库。 - 使用
javac -h
生成头文件。 - 编写并实现 C 函数。
- 编译 C 文件生成动态库。
- 运行 Java 程序,通过 JNI 调用 C 函数。