1. JNI开发环境搭建与Hello World
环境搭建目标与工具链组成
本案例要在 Linux 或 Windows 上完成一条最小可用的 JNI 工具链:JDK 提供 javac 与 java,NDK 提供交叉编译器与 JNI 头文件,构建工具负责把 C 源文件编译成动态库,最终由 JVM 在运行时加载该库并调用本地函数。三者缺一不可,且版本必须匹配。
| 组件 | 作用 | 本案例验证点 |
|---|---|---|
| JDK 17 | 编译 Java 类、运行 JVM、提供 jni.h | java -version 与 javac -version 一致 |
| NDK r26 | 提供 clang 与平台头文件 | clang --version 可执行 |
| CMake 3.22+ | 生成构建脚本,定位 NDK 工具链 | cmake --version 满足最低要求 |
工具链关系与调用路径
Linux 环境配置与验证
以下命令在 Ubuntu 22.04 上验证通过。先确认 JDK 与 NDK 路径,再设置环境变量。
# 1. 检查 JDK
java -version
javac -version
# 2. 设置 JAVA_HOME(根据实际路径调整)
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
# 3. 设置 NDK 路径
export ANDROID_NDK_HOME=$HOME/android-ndk-r26
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
# 4. 验证 clang 与 cmake
clang --version
cmake --version
若
clang --version 报错,说明 NDK 路径未加入 PATH;若 javac 找不到,检查 JAVA_HOME 是否指向 JDK 而非 JRE。Windows 环境配置要点
Windows 上推荐使用 MSYS2 或 WSL2 获得与 Linux 一致的编译体验。若使用原生 PowerShell,需将 JDK 的 bin 与 NDK 的 toolchains\llvm\prebuilt\windows-x86_64\bin 加入系统 Path,并设置 JAVA_HOME 与 ANDROID_NDK_HOME 为不含空格的路径。
# PowerShell 验证
java -version
javac -version
clang --version
cmake --version
编写最小 JNI 程序
创建 HelloJNI.java,声明一个 native 方法并在 main 中调用:
public class HelloJNI {
static {
System.loadLibrary("hello");
}
private native String sayHello();
public static void main(String[] args) {
HelloJNI app = new HelloJNI();
System.out.println(app.sayHello());
}
}
创建 hello.c,实现与 Java 类名、方法名严格对应的 JNI 函数:
#include <jni.h>
#include <stdio.h>
JNIEXPORT jstring JNICALL
Java_HelloJNI_sayHello(JNIEnv *env, jobject obj) {
return (*env)->NewStringUTF(env, "Hello from JNI!");
}
函数名必须为
Java_HelloJNI_sayHello,其中 HelloJNI 是类名,sayHello 是方法名。若包名存在,需用下划线连接,例如 Java_com_example_HelloJNI_sayHello。编译与运行
使用 CMake 组织构建,创建 CMakeLists.txt:
cmake_minimum_required(VERSION 3.22)
project(hello)
add_library(hello SHARED hello.c)
target_include_directories(hello PRIVATE
${JAVA_HOME}/include
${JAVA_HOME}/include/linux
)
执行构建与运行:
# 编译 Java
javac HelloJNI.java
# 生成构建目录并编译动态库
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build
# 将动态库放到 Java 可加载的位置
cp build/libhello.so .
# 运行
java -Djava.library.path=. HelloJNI
预期输出:
Hello from JNI!
结果判读与常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
UnsatisfiedLinkError: no hello in java.library.path | 动态库未生成或路径未指定 | 确认 libhello.so 存在,并用 -Djava.library.path 指向其目录 |
UnsatisfiedLinkError: Java_HelloJNI_sayHello | 函数名拼写错误或未导出 | 核对类名、方法名与 JNIEXPORT 声明 |
fatal error: jni.h: No such file | CMake 未包含 JDK 头文件路径 | 检查 JAVA_HOME 并确认 include/linux 存在 |
clang: command not found | NDK 工具链未加入 PATH | 将 NDK 的 prebuilt/.../bin 加入 PATH |
验证通过的标准:
java HelloJNI 输出 Hello from JNI!,且过程中无 UnsatisfiedLinkError 或编译错误。此时 JDK、NDK、CMake 三者已形成可复用的 JNI 工具链。